Commit 1d90ac41 authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://kernel.bkbits.net/jgarzik/net-drivers-2.6

into home.osdl.org:/home/torvalds/v2.5/linux
parents 50692f8c c4f0fddf
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
* - 2001/02/16 Chad N. Tindel <ctindel at ieee dot org> : * - 2001/02/16 Chad N. Tindel <ctindel at ieee dot org> :
* - Master is now brought down before setting the MAC address. In * - Master is now brought down before setting the MAC address. In
* the 2.4 kernel you can't change the MAC address while the device is * the 2.4 kernel you can't change the MAC address while the device is
* up because you get EBUSY. * up because you get EBUSY.
* *
* - 2001/09/13 Takao Indoh <indou dot takao at jp dot fujitsu dot com> * - 2001/09/13 Takao Indoh <indou dot takao at jp dot fujitsu dot com>
* - Added the ability to change the active interface on a mode 1 bond * - Added the ability to change the active interface on a mode 1 bond
...@@ -44,17 +44,60 @@ ...@@ -44,17 +44,60 @@
* *
* - 2002/10/31 Tony Cureington <tony.cureington * hp_com> : * - 2002/10/31 Tony Cureington <tony.cureington * hp_com> :
* - If the master does not have a hardware address when the first slave * - If the master does not have a hardware address when the first slave
* is enslaved, the master is assigned the hardware address of that * is enslaved, the master is assigned the hardware address of that
* slave - there is a comment in bonding.c stating "ifenslave takes * slave - there is a comment in bonding.c stating "ifenslave takes
* care of this now." This corrects the problem of slaves having * care of this now." This corrects the problem of slaves having
* different hardware addresses in active-backup mode when * different hardware addresses in active-backup mode when
* multiple interfaces are specified on a single ifenslave command * multiple interfaces are specified on a single ifenslave command
* (ifenslave bond0 eth0 eth1). * (ifenslave bond0 eth0 eth1).
* *
* - 2003/03/18 - Tsippy Mendelson <tsippy.mendelson at intel dot com> and
* Shmulik Hen <shmulik.hen at intel dot com>
* - Moved setting the slave's mac address and openning it, from
* the application to the driver. This enables support of modes
* that need to use the unique mac address of each slave.
* The driver also takes care of closing the slave and restoring its
* original mac address upon release.
* In addition, block possibility of enslaving before the master is up.
* This prevents putting the system in an undefined state.
*
* - 2003/05/01 - Amir Noam <amir.noam at intel dot com>
* - Added ABI version control to restore compatibility between
* new/old ifenslave and new/old bonding.
* - Prevent adding an adapter that is already a slave.
* Fixes the problem of stalling the transmission and leaving
* the slave in a down state.
*
* - 2003/05/01 - Shmulik Hen <shmulik.hen at intel dot com>
* - Prevent enslaving if the bond device is down.
* Fixes the problem of leaving the system in unstable state and
* halting when trying to remove the module.
* - Close socket on all abnormal exists.
* - Add versioning scheme that follows that of the bonding driver.
* current version is 1.0.0 as a base line.
*
* - 2003/05/22 - Jay Vosburgh <fubar at us dot ibm dot com>
* - ifenslave -c was broken; it's now fixed
* - Fixed problem with routes vanishing from master during enslave
* processing.
*
* - 2003/05/27 - Amir Noam <amir.noam at intel dot com>
* - Fix backward compatibility issues:
* For drivers not using ABI versions, slave was set down while
* it should be left up before enslaving.
* Also, master was not set down and the default set_mac_address()
* would fail and generate an error message in the system log.
* - For opt_c: slave should not be set to the master's setting
* while it is running. It was already set during enslave. To
* simplify things, it is now handeled separately.
*/ */
#define APP_VERSION "1.0.12"
#define APP_RELDATE "June 30, 2003"
#define APP_NAME "ifenslave"
static char *version = static char *version =
"ifenslave.c:v0.07 9/9/97 Donald Becker (becker@cesdis.gsfc.nasa.gov).\n" APP_NAME ".c:v" APP_VERSION " (" APP_RELDATE ") " "\nDonald Becker (becker@cesdis.gsfc.nasa.gov).\n"
"detach support added on 2000/10/02 by Willy Tarreau (willy at meta-x.org).\n" "detach support added on 2000/10/02 by Willy Tarreau (willy at meta-x.org).\n"
"2.4 kernel support added on 2001/02/16 by Chad N. Tindel (ctindel at ieee dot org.\n"; "2.4 kernel support added on 2001/02/16 by Chad N. Tindel (ctindel at ieee dot org.\n";
...@@ -103,6 +146,12 @@ static const char *howto_msg = ...@@ -103,6 +146,12 @@ static const char *howto_msg =
#include <linux/if_bonding.h> #include <linux/if_bonding.h>
#include <linux/sockios.h> #include <linux/sockios.h>
typedef unsigned long long u64; /* hack, so we may include kernel's ethtool.h */
typedef __uint32_t u32; /* ditto */
typedef __uint16_t u16; /* ditto */
typedef __uint8_t u8; /* ditto */
#include <linux/ethtool.h>
struct option longopts[] = { struct option longopts[] = {
/* { name has_arg *flag val } */ /* { name has_arg *flag val } */
{"all-interfaces", 0, 0, 'a'}, /* Show all interfaces. */ {"all-interfaces", 0, 0, 'a'}, /* Show all interfaces. */
...@@ -130,18 +179,19 @@ opt_howto = 0; ...@@ -130,18 +179,19 @@ opt_howto = 0;
int skfd = -1; /* AF_INET socket for ioctl() calls. */ int skfd = -1; /* AF_INET socket for ioctl() calls. */
static void if_print(char *ifname); static void if_print(char *ifname);
static int get_abi_ver(char *master_ifname);
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
struct ifreq ifr2, if_hwaddr, if_ipaddr, if_metric, if_mtu, if_dstaddr; struct ifreq ifr2, if_hwaddr, if_ipaddr, if_metric, if_mtu, if_dstaddr;
struct ifreq if_netmask, if_brdaddr, if_flags; struct ifreq if_netmask, if_brdaddr, if_flags;
int goterr = 0; int rv, goterr = 0;
int c, errflag = 0; int c, errflag = 0;
sa_family_t master_family; sa_family_t master_family;
char **spp, *master_ifname, *slave_ifname; char **spp, *master_ifname, *slave_ifname;
int hwaddr_notset; int hwaddr_notset;
int master_up; int abi_ver = 0;
while ((c = getopt_long(argc, argv, "acdfrvV?h", longopts, 0)) != EOF) while ((c = getopt_long(argc, argv, "acdfrvV?h", longopts, 0)) != EOF)
switch (c) { switch (c) {
...@@ -207,6 +257,7 @@ main(int argc, char **argv) ...@@ -207,6 +257,7 @@ main(int argc, char **argv)
char **tempp = spp; char **tempp = spp;
if ((master_ifname == NULL)||(slave_ifname == NULL)||(*tempp++ != NULL)) { if ((master_ifname == NULL)||(slave_ifname == NULL)||(*tempp++ != NULL)) {
fprintf(stderr, usage_msg); fprintf(stderr, usage_msg);
(void) close(skfd);
return 2; return 2;
} }
} }
...@@ -218,6 +269,13 @@ main(int argc, char **argv) ...@@ -218,6 +269,13 @@ main(int argc, char **argv)
exit(0); exit(0);
} }
/* exchange abi version with bonding driver */
abi_ver = get_abi_ver(master_ifname);
if (abi_ver < 0) {
(void) close(skfd);
exit(1);
}
/* Get the vitals from the master interface. */ /* Get the vitals from the master interface. */
{ {
struct ifreq *ifra[7] = { &if_ipaddr, &if_mtu, &if_dstaddr, struct ifreq *ifra[7] = { &if_ipaddr, &if_mtu, &if_dstaddr,
...@@ -242,6 +300,13 @@ main(int argc, char **argv) ...@@ -242,6 +300,13 @@ main(int argc, char **argv)
} }
} }
/* check if master is up; if not then fail any operation */
if (!(if_flags.ifr_flags & IFF_UP)) {
fprintf(stderr, "Illegal operation; the specified master interface '%s' is not up.\n", master_ifname);
(void) close(skfd);
exit (1);
}
hwaddr_notset = 1; /* assume master's address not set yet */ hwaddr_notset = 1; /* assume master's address not set yet */
for (i = 0; hwaddr_notset && (i < 6); i++) { for (i = 0; hwaddr_notset && (i < 6); i++) {
hwaddr_notset &= ((unsigned char *)if_hwaddr.ifr_hwaddr.sa_data)[i] == 0; hwaddr_notset &= ((unsigned char *)if_hwaddr.ifr_hwaddr.sa_data)[i] == 0;
...@@ -254,7 +319,7 @@ main(int argc, char **argv) ...@@ -254,7 +319,7 @@ main(int argc, char **argv)
" with ethernet-like network interfaces.\n" " with ethernet-like network interfaces.\n"
" Use the '-f' option to force the operation.\n", " Use the '-f' option to force the operation.\n",
master_ifname); master_ifname);
(void) close(skfd);
exit (1); exit (1);
} }
master_family = if_hwaddr.ifr_hwaddr.sa_family; master_family = if_hwaddr.ifr_hwaddr.sa_family;
...@@ -278,143 +343,153 @@ main(int argc, char **argv) ...@@ -278,143 +343,153 @@ main(int argc, char **argv)
fprintf(stderr, "SIOCBONDRELEASE: cannot detach %s from %s. errno=%s.\n", fprintf(stderr, "SIOCBONDRELEASE: cannot detach %s from %s. errno=%s.\n",
slave_ifname, master_ifname, strerror(errno)); slave_ifname, master_ifname, strerror(errno));
} }
else { /* we'll set the interface down to avoid any conflicts due to else if (abi_ver < 1) {
same IP/MAC */ /* The driver is using an old ABI, so we'll set the interface
* down to avoid any conflicts due to same IP/MAC
*/
strncpy(ifr2.ifr_name, slave_ifname, IFNAMSIZ); strncpy(ifr2.ifr_name, slave_ifname, IFNAMSIZ);
if (ioctl(skfd, SIOCGIFFLAGS, &ifr2) < 0) { if (ioctl(skfd, SIOCGIFFLAGS, &ifr2) < 0) {
int saved_errno = errno; int saved_errno = errno;
fprintf(stderr, "SIOCGIFFLAGS on %s failed: %s\n", slave_ifname, fprintf(stderr, "SIOCGIFFLAGS on %s failed: %s\n", slave_ifname,
strerror(saved_errno)); strerror(saved_errno));
} }
else { else {
ifr2.ifr_flags &= ~(IFF_UP | IFF_RUNNING); ifr2.ifr_flags &= ~(IFF_UP | IFF_RUNNING);
if (ioctl(skfd, SIOCSIFFLAGS, &ifr2) < 0) { if (ioctl(skfd, SIOCSIFFLAGS, &ifr2) < 0) {
int saved_errno = errno; int saved_errno = errno;
fprintf(stderr, "Shutting down interface %s failed: %s\n", fprintf(stderr, "Shutting down interface %s failed: %s\n",
slave_ifname, strerror(saved_errno)); slave_ifname, strerror(saved_errno));
} }
} }
} }
} } else if (opt_c) { /* change primary slave */
else { /* attach a slave interface to the master */ strncpy(if_flags.ifr_name, master_ifname, IFNAMSIZ);
/* two possibilities : strncpy(if_flags.ifr_slave, slave_ifname, IFNAMSIZ);
- if hwaddr_notset, do nothing. The bond will assign the if ((ioctl(skfd, SIOCBONDCHANGEACTIVE, &if_flags) < 0) &&
hwaddr from its first slave. (ioctl(skfd, BOND_CHANGE_ACTIVE_OLD, &if_flags) < 0)) {
- if !hwaddr_notset, assign the master's hwaddr to each slave fprintf(stderr, "SIOCBONDCHANGEACTIVE: %s.\n", strerror(errno));
*/ }
} else { /* attach a slave interface to the master */
strncpy(ifr2.ifr_name, slave_ifname, IFNAMSIZ); strncpy(ifr2.ifr_name, slave_ifname, IFNAMSIZ);
if (ioctl(skfd, SIOCGIFFLAGS, &ifr2) < 0) { if (ioctl(skfd, SIOCGIFFLAGS, &ifr2) < 0) {
int saved_errno = errno; int saved_errno = errno;
fprintf(stderr, "SIOCGIFFLAGS on %s failed: %s\n", slave_ifname, fprintf(stderr, "SIOCGIFFLAGS on %s failed: %s\n", slave_ifname,
strerror(saved_errno)); strerror(saved_errno));
(void) close(skfd);
return 1; return 1;
} }
if ((ifr2.ifr_flags & IFF_SLAVE) && !opt_r) {
fprintf(stderr, "%s is already a slave\n", slave_ifname);
(void) close(skfd);
return 1;
}
/* if hwaddr_notset, assign the slave hw address to the master */
if (hwaddr_notset) { if (hwaddr_notset) {
/* assign the slave hw address to the /* assign the slave hw address to the
* master since it currently does not * master since it currently does not
* have one; otherwise, slaves may * have one; otherwise, slaves may
* have different hw addresses in * have different hw addresses in
* active-backup mode as seen when enslaving * active-backup mode as seen when enslaving
* using "ifenslave bond0 eth0 eth1" because * using "ifenslave bond0 eth0 eth1" because
* hwaddr_notset is set outside this loop. * hwaddr_notset is set outside this loop.
* TODO: put this and the "else" portion in * TODO: put this and the "else" portion in
* a function. * a function.
*/ */
goterr = 0; /* get the slaves MAC address */
master_up = 0; strncpy(if_hwaddr.ifr_name, slave_ifname,
if (if_flags.ifr_flags & IFF_UP) { IFNAMSIZ);
if_flags.ifr_flags &= ~IFF_UP; rv = ioctl(skfd, SIOCGIFHWADDR, &if_hwaddr);
if (ioctl(skfd, SIOCSIFFLAGS, if (-1 == rv) {
&if_flags) < 0) { fprintf(stderr, "Could not get MAC "
goterr = 1; "address of %s: %s\n",
fprintf(stderr, slave_ifname,
"Shutting down " strerror(errno));
"interface %s failed: " strncpy(if_hwaddr.ifr_name,
"%s\n", master_ifname, IFNAMSIZ);
master_ifname, goterr = 1;
strerror(errno));
} else {
/* we took the master down,
* so we must bring it up
*/
master_up = 1;
}
} }
if (!goterr) { if (!goterr) {
/* get the slaves MAC address */ if (abi_ver < 1) {
strncpy(if_hwaddr.ifr_name, /* In ABI versions older than 1, the
slave_ifname, IFNAMSIZ); * master's set_mac routine couldn't
if (ioctl(skfd, SIOCGIFHWADDR, * work if it was up, because it
&if_hwaddr) < 0) { * used the default ethernet set_mac
fprintf(stderr, * function.
"Could not get MAC " */
"address of %s: %s\n", /* bring master down */
slave_ifname, if_flags.ifr_flags &= ~IFF_UP;
strerror(errno)); if (ioctl(skfd, SIOCSIFFLAGS,
strncpy(if_hwaddr.ifr_name, &if_flags) < 0) {
master_ifname, goterr = 1;
IFNAMSIZ); fprintf(stderr,
goterr=1; "Shutting down "
"interface %s failed: "
"%s\n",
master_ifname,
strerror(errno));
}
} }
}
if (!goterr) { strncpy(if_hwaddr.ifr_name,
strncpy(if_hwaddr.ifr_name,
master_ifname, IFNAMSIZ); master_ifname, IFNAMSIZ);
if (ioctl(skfd, SIOCSIFHWADDR, if (ioctl(skfd, SIOCSIFHWADDR,
&if_hwaddr) < 0) { &if_hwaddr) < 0) {
fprintf(stderr, fprintf(stderr,
"Could not set MAC " "Could not set MAC "
"address of %s: %s\n", "address of %s: %s\n",
master_ifname, master_ifname,
strerror(errno)); strerror(errno));
goterr=1; goterr=1;
} else { } else {
hwaddr_notset = 0; hwaddr_notset = 0;
} }
}
if (master_up) { if (abi_ver < 1) {
if_flags.ifr_flags |= IFF_UP; /* bring master back up */
if (ioctl(skfd, SIOCSIFFLAGS, if_flags.ifr_flags |= IFF_UP;
&if_flags) < 0) { if (ioctl(skfd, SIOCSIFFLAGS,
fprintf(stderr, &if_flags) < 0) {
"Bringing up interface " fprintf(stderr,
"%s failed: %s\n", "Bringing up interface "
master_ifname, "%s failed: %s\n",
strerror(errno)); master_ifname,
strerror(errno));
}
} }
} }
} else if (abi_ver < 1) { /* if (hwaddr_notset) */
} else { /* The driver is using an old ABI, so we'll set the interface
/* we'll assign master's hwaddr to this slave */ * down and assign the master's hwaddr to it
*/
if (ifr2.ifr_flags & IFF_UP) { if (ifr2.ifr_flags & IFF_UP) {
ifr2.ifr_flags &= ~IFF_UP; ifr2.ifr_flags &= ~IFF_UP;
if (ioctl(skfd, SIOCSIFFLAGS, &ifr2) < 0) { if (ioctl(skfd, SIOCSIFFLAGS, &ifr2) < 0) {
int saved_errno = errno; int saved_errno = errno;
fprintf(stderr, "Shutting down interface %s failed: %s\n", fprintf(stderr, "Shutting down interface %s failed: %s\n",
slave_ifname, strerror(saved_errno)); slave_ifname, strerror(saved_errno));
} }
} }
strncpy(if_hwaddr.ifr_name, slave_ifname, IFNAMSIZ); strncpy(if_hwaddr.ifr_name, slave_ifname, IFNAMSIZ);
if (ioctl(skfd, SIOCSIFHWADDR, &if_hwaddr) < 0) { if (ioctl(skfd, SIOCSIFHWADDR, &if_hwaddr) < 0) {
int saved_errno = errno; int saved_errno = errno;
fprintf(stderr, "SIOCSIFHWADDR on %s failed: %s\n", if_hwaddr.ifr_name, fprintf(stderr, "SIOCSIFHWADDR on %s failed: %s\n", if_hwaddr.ifr_name,
strerror(saved_errno)); strerror(saved_errno));
if (saved_errno == EBUSY) if (saved_errno == EBUSY)
fprintf(stderr, " The slave device %s is busy: it must be" fprintf(stderr, " The slave device %s is busy: it must be"
" idle before running this command.\n", slave_ifname); " idle before running this command.\n", slave_ifname);
else if (saved_errno == EOPNOTSUPP) else if (saved_errno == EOPNOTSUPP)
fprintf(stderr, " The slave device you specified does not support" fprintf(stderr, " The slave device you specified does not support"
" setting the MAC address.\n Your kernel likely does not" " setting the MAC address.\n Your kernel likely does not"
" support slave devices.\n"); " support slave devices.\n");
else if (saved_errno == EINVAL) else if (saved_errno == EINVAL)
fprintf(stderr, " The slave device's address type does not match" fprintf(stderr, " The slave device's address type does not match"
" the master's address type.\n"); " the master's address type.\n");
} else { } else {
if (verbose) { if (verbose) {
unsigned char *hwaddr = if_hwaddr.ifr_hwaddr.sa_data; unsigned char *hwaddr = if_hwaddr.ifr_hwaddr.sa_data;
...@@ -424,10 +499,11 @@ main(int argc, char **argv) ...@@ -424,10 +499,11 @@ main(int argc, char **argv)
} }
} }
} }
if (*spp && !strcmp(*spp, "metric")) { if (*spp && !strcmp(*spp, "metric")) {
if (*++spp == NULL) { if (*++spp == NULL) {
fprintf(stderr, usage_msg); fprintf(stderr, usage_msg);
(void) close(skfd);
exit(2); exit(2);
} }
if_metric.ifr_metric = atoi(*spp); if_metric.ifr_metric = atoi(*spp);
...@@ -439,7 +515,7 @@ main(int argc, char **argv) ...@@ -439,7 +515,7 @@ main(int argc, char **argv)
} }
spp++; spp++;
} }
if (strncpy(if_ipaddr.ifr_name, slave_ifname, IFNAMSIZ) <= 0 if (strncpy(if_ipaddr.ifr_name, slave_ifname, IFNAMSIZ) <= 0
|| ioctl(skfd, SIOCSIFADDR, &if_ipaddr) < 0) { || ioctl(skfd, SIOCSIFADDR, &if_ipaddr) < 0) {
fprintf(stderr, fprintf(stderr,
...@@ -452,16 +528,16 @@ main(int argc, char **argv) ...@@ -452,16 +528,16 @@ main(int argc, char **argv)
slave_ifname, ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); slave_ifname, ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);
} }
} }
if (strncpy(if_mtu.ifr_name, slave_ifname, IFNAMSIZ) <= 0 if (strncpy(if_mtu.ifr_name, slave_ifname, IFNAMSIZ) <= 0
|| ioctl(skfd, SIOCSIFMTU, &if_mtu) < 0) { || ioctl(skfd, SIOCSIFMTU, &if_mtu) < 0) {
fprintf(stderr, "Something broke setting the slave MTU: %s.\n", fprintf(stderr, "Something broke setting the slave MTU: %s.\n",
strerror(errno)); strerror(errno));
} else { } else {
if (verbose) if (verbose)
printf("Set the slave's (%s) MTU to %d.\n", slave_ifname, if_mtu.ifr_mtu); printf("Set the slave's (%s) MTU to %d.\n", slave_ifname, if_mtu.ifr_mtu);
} }
if (strncpy(if_dstaddr.ifr_name, slave_ifname, IFNAMSIZ) <= 0 if (strncpy(if_dstaddr.ifr_name, slave_ifname, IFNAMSIZ) <= 0
|| ioctl(skfd, SIOCSIFDSTADDR, &if_dstaddr) < 0) { || ioctl(skfd, SIOCSIFDSTADDR, &if_dstaddr) < 0) {
fprintf(stderr, "Error setting the slave (%s) with SIOCSIFDSTADDR: %s.\n", fprintf(stderr, "Error setting the slave (%s) with SIOCSIFDSTADDR: %s.\n",
...@@ -473,7 +549,7 @@ main(int argc, char **argv) ...@@ -473,7 +549,7 @@ main(int argc, char **argv)
slave_ifname, ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); slave_ifname, ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);
} }
} }
if (strncpy(if_brdaddr.ifr_name, slave_ifname, IFNAMSIZ) <= 0 if (strncpy(if_brdaddr.ifr_name, slave_ifname, IFNAMSIZ) <= 0
|| ioctl(skfd, SIOCSIFBRDADDR, &if_brdaddr) < 0) { || ioctl(skfd, SIOCSIFBRDADDR, &if_brdaddr) < 0) {
fprintf(stderr, fprintf(stderr,
...@@ -486,7 +562,7 @@ main(int argc, char **argv) ...@@ -486,7 +562,7 @@ main(int argc, char **argv)
slave_ifname, ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); slave_ifname, ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);
} }
} }
if (strncpy(if_netmask.ifr_name, slave_ifname, IFNAMSIZ) <= 0 if (strncpy(if_netmask.ifr_name, slave_ifname, IFNAMSIZ) <= 0
|| ioctl(skfd, SIOCSIFNETMASK, &if_netmask) < 0) { || ioctl(skfd, SIOCSIFNETMASK, &if_netmask) < 0) {
fprintf(stderr, fprintf(stderr,
...@@ -499,34 +575,45 @@ main(int argc, char **argv) ...@@ -499,34 +575,45 @@ main(int argc, char **argv)
slave_ifname, ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); slave_ifname, ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);
} }
} }
ifr2.ifr_flags |= IFF_UP; /* the interface will need to be up to be bonded */ if (abi_ver < 1) {
if ((ifr2.ifr_flags &= ~(IFF_SLAVE | IFF_MASTER)) == 0
|| strncpy(ifr2.ifr_name, slave_ifname, IFNAMSIZ) <= 0 /* The driver is using an old ABI, so we'll set the interface
|| ioctl(skfd, SIOCSIFFLAGS, &ifr2) < 0) { * up before enslaving it
fprintf(stderr, */
"Something broke setting the slave (%s) flags: %s.\n", ifr2.ifr_flags |= IFF_UP;
slave_ifname, strerror(errno)); if ((ifr2.ifr_flags &= ~(IFF_SLAVE | IFF_MASTER)) == 0
|| strncpy(ifr2.ifr_name, slave_ifname, IFNAMSIZ) <= 0
|| ioctl(skfd, SIOCSIFFLAGS, &ifr2) < 0) {
fprintf(stderr,
"Something broke setting the slave (%s) flags: %s.\n",
slave_ifname, strerror(errno));
} else {
if (verbose)
printf("Set the slave's (%s) flags %4.4x.\n",
slave_ifname, if_flags.ifr_flags);
}
} else { } else {
if (verbose) /* the bonding module takes care of setting the slave's mac address
printf("Set the slave's (%s) flags %4.4x.\n", slave_ifname, if_flags.ifr_flags); * and opening its interface
*/
if (ifr2.ifr_flags & IFF_UP) { /* the interface will need to be down */
ifr2.ifr_flags &= ~IFF_UP;
if (ioctl(skfd, SIOCSIFFLAGS, &ifr2) < 0) {
int saved_errno = errno;
fprintf(stderr, "Shutting down interface %s failed: %s\n",
slave_ifname, strerror(saved_errno));
}
}
} }
/* Do the real thing */ /* Do the real thing */
if ( ! opt_r) { if (!opt_r) {
strncpy(if_flags.ifr_name, master_ifname, IFNAMSIZ); strncpy(if_flags.ifr_name, master_ifname, IFNAMSIZ);
strncpy(if_flags.ifr_slave, slave_ifname, IFNAMSIZ); strncpy(if_flags.ifr_slave, slave_ifname, IFNAMSIZ);
if (!opt_c) { if ((ioctl(skfd, SIOCBONDENSLAVE, &if_flags) < 0) &&
if ((ioctl(skfd, SIOCBONDENSLAVE, &if_flags) < 0) && (ioctl(skfd, BOND_ENSLAVE_OLD, &if_flags) < 0)) {
(ioctl(skfd, BOND_ENSLAVE_OLD, &if_flags) < 0)) { fprintf(stderr, "SIOCBONDENSLAVE: %s.\n", strerror(errno));
fprintf(stderr, "SIOCBONDENSLAVE: %s.\n", strerror(errno));
}
}
else {
if ((ioctl(skfd, SIOCBONDCHANGEACTIVE, &if_flags) < 0) &&
(ioctl(skfd, BOND_CHANGE_ACTIVE_OLD, &if_flags) < 0)) {
fprintf(stderr, "SIOCBONDCHANGEACTIVE: %s.\n", strerror(errno));
}
} }
} }
} }
...@@ -540,7 +627,7 @@ main(int argc, char **argv) ...@@ -540,7 +627,7 @@ main(int argc, char **argv)
static short mif_flags; static short mif_flags;
/* Get the interface configuration from the kernel. */ /* Get the inteface configuration from the kernel. */
static int if_getconfig(char *ifname) static int if_getconfig(char *ifname)
{ {
struct ifreq ifr; struct ifreq ifr;
...@@ -639,7 +726,38 @@ static void if_print(char *ifname) ...@@ -639,7 +726,38 @@ static void if_print(char *ifname)
} }
} }
static int get_abi_ver(char *master_ifname)
{
struct ifreq ifr;
struct ethtool_drvinfo info;
int abi_ver = 0;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ);
ifr.ifr_data = (caddr_t)&info;
info.cmd = ETHTOOL_GDRVINFO;
strncpy(info.driver, "ifenslave", 32);
snprintf(info.fw_version, 32, "%d", BOND_ABI_VERSION);
if (ioctl(skfd, SIOCETHTOOL, &ifr) >= 0) {
char *endptr;
abi_ver = strtoul(info.fw_version, &endptr, 0);
if (*endptr) {
fprintf(stderr, "Error: got invalid string as an ABI "
"version from the bonding module\n");
return -1;
}
}
if (verbose) {
printf("ABI ver is %d\n", abi_ver);
}
return abi_ver;
}
/* /*
* Local variables: * Local variables:
* version-control: t * version-control: t
......
...@@ -420,8 +420,8 @@ L: linux-computone@lazuli.wittsend.com ...@@ -420,8 +420,8 @@ L: linux-computone@lazuli.wittsend.com
S: Supported S: Supported
COMX/MULTIGATE SYNC SERIAL DRIVERS COMX/MULTIGATE SYNC SERIAL DRIVERS
P: Gergely Madarasz P: Pasztor Szilard
M: Gergely Madarasz <gorgo@itc.hu> M: Pasztor Szilard <don@itc.hu>
S: Supported S: Supported
COSA/SRP SYNC SERIAL DRIVER COSA/SRP SYNC SERIAL DRIVER
......
...@@ -110,6 +110,7 @@ ...@@ -110,6 +110,7 @@
#include <linux/mii.h> #include <linux/mii.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/crc32.h> #include <linux/crc32.h>
#include <linux/suspend.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -1597,6 +1598,9 @@ static int rtl8139_thread (void *data) ...@@ -1597,6 +1598,9 @@ static int rtl8139_thread (void *data)
timeout = next_tick; timeout = next_tick;
do { do {
timeout = interruptible_sleep_on_timeout (&tp->thr_wait, timeout); timeout = interruptible_sleep_on_timeout (&tp->thr_wait, timeout);
/* make swsusp happy with our thread */
if (current->flags & PF_FREEZE)
refrigerator(PF_IOTHREAD);
} while (!signal_pending (current) && (timeout > 0)); } while (!signal_pending (current) && (timeout > 0));
if (signal_pending (current)) { if (signal_pending (current)) {
......
/* b44.c: Broadcom 4400 device driver. /* b44.c: Broadcom 4400 device driver.
* *
* Copyright (C) 2002 David S. Miller (davem@redhat.com) * Copyright (C) 2002 David S. Miller (davem@redhat.com)
* Fixed by Pekka Pietikainen (pp@ee.oulu.fi)
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -14,6 +15,7 @@ ...@@ -14,6 +15,7 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/version.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -23,8 +25,8 @@ ...@@ -23,8 +25,8 @@
#define DRV_MODULE_NAME "b44" #define DRV_MODULE_NAME "b44"
#define PFX DRV_MODULE_NAME ": " #define PFX DRV_MODULE_NAME ": "
#define DRV_MODULE_VERSION "0.6" #define DRV_MODULE_VERSION "0.9"
#define DRV_MODULE_RELDATE "Nov 11, 2002" #define DRV_MODULE_RELDATE "Jul 14, 2003"
#define B44_DEF_MSG_ENABLE \ #define B44_DEF_MSG_ENABLE \
(NETIF_MSG_DRV | \ (NETIF_MSG_DRV | \
...@@ -78,6 +80,15 @@ MODULE_PARM_DESC(b44_debug, "B44 bitmapped debugging message enable value"); ...@@ -78,6 +80,15 @@ MODULE_PARM_DESC(b44_debug, "B44 bitmapped debugging message enable value");
static int b44_debug = -1; /* -1 == use B44_DEF_MSG_ENABLE as value */ static int b44_debug = -1; /* -1 == use B44_DEF_MSG_ENABLE as value */
#ifndef PCI_DEVICE_ID_BCM4401
#define PCI_DEVICE_ID_BCM4401 0x4401
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
#define IRQ_RETVAL(x)
#define irqreturn_t void
#endif
static struct pci_device_id b44_pci_tbl[] __devinitdata = { static struct pci_device_id b44_pci_tbl[] __devinitdata = {
{ PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
...@@ -259,7 +270,7 @@ static int ssb_is_core_up(struct b44 *bp) ...@@ -259,7 +270,7 @@ static int ssb_is_core_up(struct b44 *bp)
== SBTMSLOW_CLOCK); == SBTMSLOW_CLOCK);
} }
static void __b44_cam_write(struct b44 *bp, char *data, int index) static void __b44_cam_write(struct b44 *bp, unsigned char *data, int index)
{ {
u32 val; u32 val;
...@@ -521,7 +532,7 @@ static void b44_check_phy(struct b44 *bp) ...@@ -521,7 +532,7 @@ static void b44_check_phy(struct b44 *bp)
/* Link now up */ /* Link now up */
netif_carrier_on(bp->dev); netif_carrier_on(bp->dev);
b44_link_report(bp); b44_link_report(bp);
} else if (netif_carrier_ok(bp->dev)) { } else if (netif_carrier_ok(bp->dev) && !(bmsr & BMSR_LSTATUS)) {
/* Link now down */ /* Link now down */
netif_carrier_off(bp->dev); netif_carrier_off(bp->dev);
b44_link_report(bp); b44_link_report(bp);
...@@ -650,8 +661,7 @@ static void b44_recycle_rx(struct b44 *bp, int src_idx, u32 dest_idx_unmasked) ...@@ -650,8 +661,7 @@ static void b44_recycle_rx(struct b44 *bp, int src_idx, u32 dest_idx_unmasked)
src_map = &bp->rx_buffers[src_idx]; src_map = &bp->rx_buffers[src_idx];
dest_map->skb = src_map->skb; dest_map->skb = src_map->skb;
rh = (struct rx_header *) rh = (struct rx_header *) src_map->skb->data;
(src_map->skb->data - bp->rx_offset);
rh->len = 0; rh->len = 0;
rh->flags = 0; rh->flags = 0;
pci_unmap_addr_set(dest_map, mapping, pci_unmap_addr_set(dest_map, mapping,
...@@ -660,9 +670,12 @@ static void b44_recycle_rx(struct b44 *bp, int src_idx, u32 dest_idx_unmasked) ...@@ -660,9 +670,12 @@ static void b44_recycle_rx(struct b44 *bp, int src_idx, u32 dest_idx_unmasked)
ctrl = src_desc->ctrl; ctrl = src_desc->ctrl;
if (dest_idx == (B44_RX_RING_SIZE - 1)) if (dest_idx == (B44_RX_RING_SIZE - 1))
ctrl |= cpu_to_le32(DESC_CTRL_EOT); ctrl |= cpu_to_le32(DESC_CTRL_EOT);
else
ctrl &= cpu_to_le32(~DESC_CTRL_EOT);
dest_desc->ctrl = ctrl; dest_desc->ctrl = ctrl;
dest_desc->addr = src_desc->addr; dest_desc->addr = src_desc->addr;
src_map->skb = NULL;
} }
static int b44_rx(struct b44 *bp, int budget) static int b44_rx(struct b44 *bp, int budget)
...@@ -685,7 +698,7 @@ static int b44_rx(struct b44 *bp, int budget) ...@@ -685,7 +698,7 @@ static int b44_rx(struct b44 *bp, int budget)
pci_dma_sync_single(bp->pdev, map, pci_dma_sync_single(bp->pdev, map,
RX_PKT_BUF_SZ, RX_PKT_BUF_SZ,
PCI_DMA_FROMDEVICE); PCI_DMA_FROMDEVICE);
rh = (struct rx_header *) (skb->data - bp->rx_offset); rh = (struct rx_header *) skb->data;
len = cpu_to_le16(rh->len); len = cpu_to_le16(rh->len);
if ((len > (RX_PKT_BUF_SZ - bp->rx_offset)) || if ((len > (RX_PKT_BUF_SZ - bp->rx_offset)) ||
(rh->flags & cpu_to_le16(RX_FLAG_ERRORS))) { (rh->flags & cpu_to_le16(RX_FLAG_ERRORS))) {
...@@ -718,7 +731,9 @@ static int b44_rx(struct b44 *bp, int budget) ...@@ -718,7 +731,9 @@ static int b44_rx(struct b44 *bp, int budget)
goto drop_it; goto drop_it;
pci_unmap_single(bp->pdev, map, pci_unmap_single(bp->pdev, map,
skb_size, PCI_DMA_FROMDEVICE); skb_size, PCI_DMA_FROMDEVICE);
skb_put(skb, len); /* Leave out rx_header */
skb_put(skb, len+bp->rx_offset);
skb_pull(skb,bp->rx_offset);
} else { } else {
struct sk_buff *copy_skb; struct sk_buff *copy_skb;
...@@ -730,8 +745,8 @@ static int b44_rx(struct b44 *bp, int budget) ...@@ -730,8 +745,8 @@ static int b44_rx(struct b44 *bp, int budget)
copy_skb->dev = bp->dev; copy_skb->dev = bp->dev;
skb_reserve(copy_skb, 2); skb_reserve(copy_skb, 2);
skb_put(copy_skb, len); skb_put(copy_skb, len);
/* DMA sync done above */ /* DMA sync done above, copy just the actual packet */
memcpy(copy_skb->data, skb->data, len); memcpy(copy_skb->data, skb->data+bp->rx_offset, len);
skb = copy_skb; skb = copy_skb;
} }
...@@ -748,6 +763,7 @@ static int b44_rx(struct b44 *bp, int budget) ...@@ -748,6 +763,7 @@ static int b44_rx(struct b44 *bp, int budget)
} }
bp->rx_cons = cons; bp->rx_cons = cons;
bw32(B44_DMARX_PTR, cons * sizeof(struct dma_desc));
return received; return received;
} }
...@@ -764,6 +780,7 @@ static int b44_poll(struct net_device *netdev, int *budget) ...@@ -764,6 +780,7 @@ static int b44_poll(struct net_device *netdev, int *budget)
b44_tx(bp); b44_tx(bp);
/* spin_unlock(&bp->tx_lock); */ /* spin_unlock(&bp->tx_lock); */
} }
spin_unlock_irq(&bp->lock);
done = 1; done = 1;
if (bp->istat & ISTAT_RX) { if (bp->istat & ISTAT_RX) {
...@@ -783,10 +800,12 @@ static int b44_poll(struct net_device *netdev, int *budget) ...@@ -783,10 +800,12 @@ static int b44_poll(struct net_device *netdev, int *budget)
} }
if (bp->istat & ISTAT_ERRORS) { if (bp->istat & ISTAT_ERRORS) {
spin_lock_irq(&bp->lock);
b44_halt(bp); b44_halt(bp);
b44_init_rings(bp); b44_init_rings(bp);
b44_init_hw(bp); b44_init_hw(bp);
netif_wake_queue(bp->dev); netif_wake_queue(bp->dev);
spin_unlock_irq(&bp->lock);
done = 1; done = 1;
} }
...@@ -794,7 +813,6 @@ static int b44_poll(struct net_device *netdev, int *budget) ...@@ -794,7 +813,6 @@ static int b44_poll(struct net_device *netdev, int *budget)
netif_rx_complete(netdev); netif_rx_complete(netdev);
b44_enable_ints(bp); b44_enable_ints(bp);
} }
spin_unlock_irq(&bp->lock);
return (done ? 0 : 1); return (done ? 0 : 1);
} }
...@@ -885,7 +903,7 @@ static int b44_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -885,7 +903,7 @@ static int b44_start_xmit(struct sk_buff *skb, struct net_device *dev)
ctrl |= DESC_CTRL_EOT; ctrl |= DESC_CTRL_EOT;
bp->tx_ring[entry].ctrl = cpu_to_le32(ctrl); bp->tx_ring[entry].ctrl = cpu_to_le32(ctrl);
bp->tx_ring[entry].addr = cpu_to_le32((u32) mapping); bp->tx_ring[entry].addr = cpu_to_le32((u32) mapping+bp->dma_offset);
entry = NEXT_TX(entry); entry = NEXT_TX(entry);
...@@ -1173,8 +1191,8 @@ static int b44_init_hw(struct b44 *bp) ...@@ -1173,8 +1191,8 @@ static int b44_init_hw(struct b44 *bp)
__b44_set_rx_mode(bp->dev); __b44_set_rx_mode(bp->dev);
/* MTU + eth header + possible VLAN tag + struct rx_header */ /* MTU + eth header + possible VLAN tag + struct rx_header */
bw32(B44_RXMAXLEN, bp->dev->mtu + ETH_HLEN + 8 + 24); bw32(B44_RXMAXLEN, bp->dev->mtu + ETH_HLEN + 8 + RX_HEADER_LEN);
bw32(B44_TXMAXLEN, bp->dev->mtu + ETH_HLEN + 8 + 24); bw32(B44_TXMAXLEN, bp->dev->mtu + ETH_HLEN + 8 + RX_HEADER_LEN);
bw32(B44_TX_WMARK, 56); /* XXX magic */ bw32(B44_TX_WMARK, 56); /* XXX magic */
bw32(B44_DMATX_CTRL, DMATX_CTRL_ENABLE); bw32(B44_DMATX_CTRL, DMATX_CTRL_ENABLE);
...@@ -1184,6 +1202,7 @@ static int b44_init_hw(struct b44 *bp) ...@@ -1184,6 +1202,7 @@ static int b44_init_hw(struct b44 *bp)
bw32(B44_DMARX_ADDR, bp->rx_ring_dma + bp->dma_offset); bw32(B44_DMARX_ADDR, bp->rx_ring_dma + bp->dma_offset);
bw32(B44_DMARX_PTR, bp->rx_pending); bw32(B44_DMARX_PTR, bp->rx_pending);
bp->rx_prod = bp->rx_pending;
bw32(B44_MIB_CTRL, MIB_CTRL_CLR_ON_READ); bw32(B44_MIB_CTRL, MIB_CTRL_CLR_ON_READ);
...@@ -1345,6 +1364,8 @@ static void __b44_set_rx_mode(struct net_device *dev) ...@@ -1345,6 +1364,8 @@ static void __b44_set_rx_mode(struct net_device *dev)
__b44_load_mcast(bp, dev); __b44_load_mcast(bp, dev);
bw32(B44_RXCONFIG, val); bw32(B44_RXCONFIG, val);
val = br32(B44_CAM_CTRL);
bw32(B44_CAM_CTRL, val | CAM_CTRL_ENABLE);
} }
} }
...@@ -1678,8 +1699,9 @@ static int __devinit b44_get_invariants(struct b44 *bp) ...@@ -1678,8 +1699,9 @@ static int __devinit b44_get_invariants(struct b44 *bp)
bp->core_unit = ssb_core_unit(bp); bp->core_unit = ssb_core_unit(bp);
bp->dma_offset = ssb_get_addr(bp, SBID_PCI_DMA, 0); bp->dma_offset = ssb_get_addr(bp, SBID_PCI_DMA, 0);
bp->flags |= B44_FLAG_BUGGY_TXPTR; /* XXX - really required?
bp->flags |= B44_FLAG_BUGGY_TXPTR;
*/
out: out:
return err; return err;
} }
......
...@@ -142,7 +142,7 @@ ...@@ -142,7 +142,7 @@
#define MDIO_OP_READ 2 #define MDIO_OP_READ 2
#define MDIO_DATA_SB_MASK 0xc0000000 /* Start Bits */ #define MDIO_DATA_SB_MASK 0xc0000000 /* Start Bits */
#define MDIO_DATA_SB_SHIFT 30 #define MDIO_DATA_SB_SHIFT 30
#define MDIO_DATA_SB_START 0x10000000 /* Start Of Frame */ #define MDIO_DATA_SB_START 0x40000000 /* Start Of Frame */
#define B44_EMAC_IMASK 0x0418UL /* EMAC Interrupt Mask */ #define B44_EMAC_IMASK 0x0418UL /* EMAC Interrupt Mask */
#define B44_EMAC_ISTAT 0x041CUL /* EMAC Interrupt Status */ #define B44_EMAC_ISTAT 0x041CUL /* EMAC Interrupt Status */
#define EMAC_INT_MII 0x00000001 /* MII MDIO Interrupt */ #define EMAC_INT_MII 0x00000001 /* MII MDIO Interrupt */
......
...@@ -1475,6 +1475,107 @@ e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr) ...@@ -1475,6 +1475,107 @@ e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr)
return -EFAULT; return -EFAULT;
return 0; return 0;
} }
case ETHTOOL_GRXCSUM: {
struct ethtool_value edata = { ETHTOOL_GRXCSUM };
edata.data = adapter->rx_csum;
if (copy_to_user(addr, &edata, sizeof(edata)))
return -EFAULT;
return 0;
}
case ETHTOOL_SRXCSUM: {
struct ethtool_value edata;
if (copy_from_user(&edata, addr, sizeof(edata)))
return -EFAULT;
adapter->rx_csum = edata.data;
if(netif_running(netdev)) {
e1000_down(adapter);
e1000_up(adapter);
} else
e1000_reset(adapter);
return 0;
}
case ETHTOOL_GTXCSUM: {
struct ethtool_value edata = { ETHTOOL_GTXCSUM };
edata.data =
(netdev->features & NETIF_F_HW_CSUM) != 0;
if (copy_to_user(addr, &edata, sizeof(edata)))
return -EFAULT;
return 0;
}
case ETHTOOL_STXCSUM: {
struct ethtool_value edata;
if (copy_from_user(&edata, addr, sizeof(edata)))
return -EFAULT;
if(adapter->hw.mac_type < e1000_82543) {
if (edata.data != 0)
return -EINVAL;
return 0;
}
if (edata.data)
netdev->features |= NETIF_F_HW_CSUM;
else
netdev->features &= ~NETIF_F_HW_CSUM;
return 0;
}
case ETHTOOL_GSG: {
struct ethtool_value edata = { ETHTOOL_GSG };
edata.data =
(netdev->features & NETIF_F_SG) != 0;
if (copy_to_user(addr, &edata, sizeof(edata)))
return -EFAULT;
return 0;
}
case ETHTOOL_SSG: {
struct ethtool_value edata;
if (copy_from_user(&edata, addr, sizeof(edata)))
return -EFAULT;
if (edata.data)
netdev->features |= NETIF_F_SG;
else
netdev->features &= ~NETIF_F_SG;
return 0;
}
#ifdef NETIF_F_TSO
case ETHTOOL_GTSO: {
struct ethtool_value edata = { ETHTOOL_GTSO };
edata.data = (netdev->features & NETIF_F_TSO) != 0;
if (copy_to_user(addr, &edata, sizeof(edata)))
return -EFAULT;
return 0;
}
case ETHTOOL_STSO: {
struct ethtool_value edata;
if (copy_from_user(&edata, addr, sizeof(edata)))
return -EFAULT;
if ((adapter->hw.mac_type < e1000_82544) ||
(adapter->hw.mac_type == e1000_82547)) {
if (edata.data != 0)
return -EINVAL;
return 0;
}
if (edata.data)
netdev->features |= NETIF_F_TSO;
else
netdev->features &= ~NETIF_F_TSO;
return 0;
}
#endif
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
char e1000_driver_name[] = "e1000"; char e1000_driver_name[] = "e1000";
char e1000_driver_string[] = "Intel(R) PRO/1000 Network Driver"; char e1000_driver_string[] = "Intel(R) PRO/1000 Network Driver";
char e1000_driver_version[] = "5.1.13-k1"; char e1000_driver_version[] = "5.1.13-k2";
char e1000_copyright[] = "Copyright (c) 1999-2003 Intel Corporation."; char e1000_copyright[] = "Copyright (c) 1999-2003 Intel Corporation.";
/* e1000_pci_tbl - PCI Device ID Table /* e1000_pci_tbl - PCI Device ID Table
......
...@@ -635,6 +635,7 @@ static void __devexit ne2k_pci_remove_one (struct pci_dev *pdev) ...@@ -635,6 +635,7 @@ static void __devexit ne2k_pci_remove_one (struct pci_dev *pdev)
unregister_netdev(dev); unregister_netdev(dev);
release_region(dev->base_addr, NE_IO_EXTENT); release_region(dev->base_addr, NE_IO_EXTENT);
kfree(dev->priv);
kfree(dev); kfree(dev);
pci_set_drvdata(pdev, NULL); pci_set_drvdata(pdev, NULL);
} }
......
...@@ -296,7 +296,8 @@ static dev_link_t *tc574_attach(void) ...@@ -296,7 +296,8 @@ static dev_link_t *tc574_attach(void)
lp = dev->priv; lp = dev->priv;
link = &lp->link; link = &lp->link;
link->priv = dev; link->priv = dev;
spin_lock_init(&lp->window_lock);
init_timer(&link->release); init_timer(&link->release);
link->release.function = &tc574_release; link->release.function = &tc574_release;
link->release.data = (unsigned long)link; link->release.data = (unsigned long)link;
......
...@@ -254,8 +254,7 @@ static void SetLANCE(struct SKMCA_NETDEV *dev, u16 addr, u16 value) ...@@ -254,8 +254,7 @@ static void SetLANCE(struct SKMCA_NETDEV *dev, u16 addr, u16 value)
/* disable interrupts */ /* disable interrupts */
save_flags(flags); spin_lock_irqsave(&priv->lock, flags);
cli();
/* wait until no transfer is pending */ /* wait until no transfer is pending */
...@@ -281,7 +280,7 @@ static void SetLANCE(struct SKMCA_NETDEV *dev, u16 addr, u16 value) ...@@ -281,7 +280,7 @@ static void SetLANCE(struct SKMCA_NETDEV *dev, u16 addr, u16 value)
/* reenable interrupts */ /* reenable interrupts */
restore_flags(flags); spin_lock_irqrestore(&priv->lock, flags);
} }
/* get LANCE register */ /* get LANCE register */
...@@ -294,8 +293,7 @@ static u16 GetLANCE(struct SKMCA_NETDEV *dev, u16 addr) ...@@ -294,8 +293,7 @@ static u16 GetLANCE(struct SKMCA_NETDEV *dev, u16 addr)
/* disable interrupts */ /* disable interrupts */
save_flags(flags); spin_lock_irqsave(&priv->lock, flags);
cli();
/* wait until no transfer is pending */ /* wait until no transfer is pending */
...@@ -321,7 +319,7 @@ static u16 GetLANCE(struct SKMCA_NETDEV *dev, u16 addr) ...@@ -321,7 +319,7 @@ static u16 GetLANCE(struct SKMCA_NETDEV *dev, u16 addr)
/* reenable interrupts */ /* reenable interrupts */
restore_flags(flags); spin_lock_irqrestore(&priv->lock, flags);
return res; return res;
} }
...@@ -968,8 +966,9 @@ static int skmca_tx(struct sk_buff *skb, struct SKMCA_NETDEV *dev) ...@@ -968,8 +966,9 @@ static int skmca_tx(struct sk_buff *skb, struct SKMCA_NETDEV *dev)
#endif #endif
/* one more descriptor busy */ /* one more descriptor busy */
save_flags(flags);
cli(); spin_lock_irqsave(&priv->lock, flags);
priv->nexttxput++; priv->nexttxput++;
if (priv->nexttxput >= TXCOUNT) if (priv->nexttxput >= TXCOUNT)
priv->nexttxput = 0; priv->nexttxput = 0;
...@@ -994,7 +993,7 @@ static int skmca_tx(struct sk_buff *skb, struct SKMCA_NETDEV *dev) ...@@ -994,7 +993,7 @@ static int skmca_tx(struct sk_buff *skb, struct SKMCA_NETDEV *dev)
if (priv->txbusy == 0) if (priv->txbusy == 0)
SetLANCE(dev, LANCE_CSR0, CSR0_INEA | CSR0_TDMD); SetLANCE(dev, LANCE_CSR0, CSR0_INEA | CSR0_TDMD);
restore_flags(flags); spin_lock_irqrestore(&priv->lock, flags);
tx_done: tx_done:
......
...@@ -53,6 +53,7 @@ typedef struct { ...@@ -53,6 +53,7 @@ typedef struct {
int realirq; /* memorizes actual IRQ, even when int realirq; /* memorizes actual IRQ, even when
currently not allocated */ currently not allocated */
skmca_medium medium; /* physical cannector */ skmca_medium medium; /* physical cannector */
spinlock_t lock;
} skmca_priv; } skmca_priv;
/* card registers: control/status register bits */ /* card registers: control/status register bits */
......
...@@ -122,11 +122,14 @@ ...@@ -122,11 +122,14 @@
- No filtering multicast in promisc mode (Edward Peng) - No filtering multicast in promisc mode (Edward Peng)
- Fix for Rhine-I Tx timeouts - Fix for Rhine-I Tx timeouts
LK1.1.19 (Roger Luethi)
- Increase Tx threshold for unspecified errors
*/ */
#define DRV_NAME "via-rhine" #define DRV_NAME "via-rhine"
#define DRV_VERSION "1.1.18-2.5" #define DRV_VERSION "1.1.19-2.5"
#define DRV_RELDATE "July-4-2003" #define DRV_RELDATE "July-12-2003"
/* A few user-configurable values. /* A few user-configurable values.
...@@ -1664,9 +1667,13 @@ static void via_rhine_error(struct net_device *dev, int intr_status) ...@@ -1664,9 +1667,13 @@ static void via_rhine_error(struct net_device *dev, int intr_status)
} }
if ((intr_status & IntrTxError) && ~( IntrTxAborted | IntrTxUnderrun | if ((intr_status & IntrTxError) && ~( IntrTxAborted | IntrTxUnderrun |
IntrTxDescRace )) { IntrTxDescRace )) {
if (debug > 2) if (np->tx_thresh < 0xE0) {
printk(KERN_INFO "%s: Unspecified error.\n", writeb(np->tx_thresh += 0x20, ioaddr + TxConfig);
dev->name); }
if (debug > 1)
printk(KERN_INFO "%s: Unspecified error. Tx "
"threshold now %2.2x.\n",
dev->name, np->tx_thresh);
} }
if (intr_status & ( IntrTxAborted | IntrTxUnderrun | IntrTxDescRace | if (intr_status & ( IntrTxAborted | IntrTxUnderrun | IntrTxDescRace |
IntrTxError )) IntrTxError ))
......
...@@ -60,9 +60,10 @@ config COSA ...@@ -60,9 +60,10 @@ config COSA
# #
# COMX drivers # COMX drivers
# #
# Not updated to 2.6.
config COMX config COMX
tristate "MultiGate (COMX) synchronous serial boards support" tristate "MultiGate (COMX) synchronous serial boards support"
depends on WAN && (ISA || PCI) depends on WAN && (ISA || PCI) && OBSOLETE
---help--- ---help---
Say Y if you want to use any board from the MultiGate (COMX) family. Say Y if you want to use any board from the MultiGate (COMX) family.
These boards are synchronous serial adapters for the PC, These boards are synchronous serial adapters for the PC,
......
...@@ -296,6 +296,14 @@ config PCMCIA_ATMEL ...@@ -296,6 +296,14 @@ config PCMCIA_ATMEL
firmware package can be downloaded from firmware package can be downloaded from
http://www.thekelleys.org.uk/atmel/atmel_firmware.tar.gz http://www.thekelleys.org.uk/atmel/atmel_firmware.tar.gz
config PCMCIA_WL3501
tristate "Planet WL3501 PCMCIA cards"
depends on NET_RADIO && EXPERIMENTAL && PCMCIA
---help---
A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet.
It has basic support for Linux wireless extensions and initial
micro support for ethtool.
# yes, this works even when no drivers are selected # yes, this works even when no drivers are selected
config NET_WIRELESS config NET_WIRELESS
bool bool
......
...@@ -23,4 +23,4 @@ obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o ...@@ -23,4 +23,4 @@ obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o
# 16-bit wireless PCMCIA client drivers # 16-bit wireless PCMCIA client drivers
obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o atmel.o obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o atmel.o
obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o
...@@ -648,9 +648,38 @@ typedef struct { ...@@ -648,9 +648,38 @@ typedef struct {
u16 currentXmitRate; u16 currentXmitRate;
u16 apDevExtensions; u16 apDevExtensions;
u16 normalizedSignalStrength; u16 normalizedSignalStrength;
u16 _reserved1; u16 shortPreamble;
u8 apIP[4]; u8 apIP[4];
u16 _reserved[7]; u8 noisePercent; /* Noise percent in last second */
u8 noisedBm; /* Noise dBm in last second */
u8 noiseAvePercent; /* Noise percent in last minute */
u8 noiseAvedBm; /* Noise dBm in last minute */
u8 noiseMaxPercent; /* Highest noise percent in last minute */
u8 noiseMaxdBm; /* Highest noise dbm in last minute */
u16 load;
u8 carrier[4];
u16 assocStatus;
#define STAT_NOPACKETS 0
#define STAT_NOCARRIERSET 10
#define STAT_GOTCARRIERSET 11
#define STAT_WRONGSSID 20
#define STAT_BADCHANNEL 25
#define STAT_BADBITRATES 30
#define STAT_BADPRIVACY 35
#define STAT_APFOUND 40
#define STAT_APREJECTED 50
#define STAT_AUTHENTICATING 60
#define STAT_DEAUTHENTICATED 61
#define STAT_AUTHTIMEOUT 62
#define STAT_ASSOCIATING 70
#define STAT_DEASSOCIATED 71
#define STAT_ASSOCTIMEOUT 72
#define STAT_NOTAIROAP 73
#define STAT_ASSOCIATED 80
#define STAT_LEAPING 90
#define STAT_LEAPFAILED 91
#define STAT_LEAPTIMEDOUT 92
#define STAT_LEAPCOMPLETE 93
} StatusRid; } StatusRid;
typedef struct { typedef struct {
...@@ -923,8 +952,8 @@ static int get_dec_u16( char *buffer, int *start, int limit ); ...@@ -923,8 +952,8 @@ static int get_dec_u16( char *buffer, int *start, int limit );
static void OUT4500( struct airo_info *, u16 register, u16 value ); static void OUT4500( struct airo_info *, u16 register, u16 value );
static unsigned short IN4500( struct airo_info *, u16 register ); static unsigned short IN4500( struct airo_info *, u16 register );
static u16 setup_card(struct airo_info*, u8 *mac); static u16 setup_card(struct airo_info*, u8 *mac);
static int enable_MAC( struct airo_info *ai, Resp *rsp ); static int enable_MAC( struct airo_info *ai, Resp *rsp, int lock );
static void disable_MAC(struct airo_info *ai); static void disable_MAC(struct airo_info *ai, int lock);
static void enable_interrupts(struct airo_info*); static void enable_interrupts(struct airo_info*);
static void disable_interrupts(struct airo_info*); static void disable_interrupts(struct airo_info*);
static u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp); static u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp);
...@@ -938,11 +967,11 @@ static int fast_bap_read(struct airo_info*, u16 *pu16Dst, int bytelen, ...@@ -938,11 +967,11 @@ static int fast_bap_read(struct airo_info*, u16 *pu16Dst, int bytelen,
static int bap_write(struct airo_info*, const u16 *pu16Src, int bytelen, static int bap_write(struct airo_info*, const u16 *pu16Src, int bytelen,
int whichbap); int whichbap);
static int PC4500_accessrid(struct airo_info*, u16 rid, u16 accmd); static int PC4500_accessrid(struct airo_info*, u16 rid, u16 accmd);
static int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len); static int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len, int lock);
static int PC4500_writerid(struct airo_info*, u16 rid, const void static int PC4500_writerid(struct airo_info*, u16 rid, const void
*pBuf, int len); *pBuf, int len, int lock);
static int do_writerid( struct airo_info*, u16 rid, const void *rid_data, static int do_writerid( struct airo_info*, u16 rid, const void *rid_data,
int len ); int len, int dummy );
static u16 transmit_allocate(struct airo_info*, int lenPayload, int raw); static u16 transmit_allocate(struct airo_info*, int lenPayload, int raw);
static int transmit_802_3_packet(struct airo_info*, int len, char *pPacket); static int transmit_802_3_packet(struct airo_info*, int len, char *pPacket);
static int transmit_802_11_packet(struct airo_info*, int len, char *pPacket); static int transmit_802_11_packet(struct airo_info*, int len, char *pPacket);
...@@ -986,7 +1015,6 @@ struct airo_info { ...@@ -986,7 +1015,6 @@ struct airo_info {
#define FLAG_PROMISC IFF_PROMISC /* 0x100 - include/linux/if.h */ #define FLAG_PROMISC IFF_PROMISC /* 0x100 - include/linux/if.h */
#define FLAG_RADIO_OFF 0x02 /* User disabling of MAC */ #define FLAG_RADIO_OFF 0x02 /* User disabling of MAC */
#define FLAG_RADIO_DOWN 0x08 /* ifup/ifdown disabling of MAC */ #define FLAG_RADIO_DOWN 0x08 /* ifup/ifdown disabling of MAC */
#define FLAG_LOCKED 2 /* 0x04 - use as a bit offset */
#define FLAG_FLASHING 0x10 #define FLAG_FLASHING 0x10
#define FLAG_ADHOC 0x01 /* Needed by MIC */ #define FLAG_ADHOC 0x01 /* Needed by MIC */
#define FLAG_MIC_CAPABLE 0x20 #define FLAG_MIC_CAPABLE 0x20
...@@ -999,6 +1027,7 @@ struct airo_info { ...@@ -999,6 +1027,7 @@ struct airo_info {
tdsRssiEntry *rssi; tdsRssiEntry *rssi;
struct semaphore sem; struct semaphore sem;
struct task_struct *task; struct task_struct *task;
struct work_struct stats_task;
struct work_struct promisc_task; struct work_struct promisc_task;
struct { struct {
struct sk_buff *skb; struct sk_buff *skb;
...@@ -1056,7 +1085,7 @@ static int readBSSListRid(struct airo_info *ai, int first, ...@@ -1056,7 +1085,7 @@ static int readBSSListRid(struct airo_info *ai, int first,
ai->task = NULL; ai->task = NULL;
} }
rc = PC4500_readrid(ai, first ? RID_BSSLISTFIRST : RID_BSSLISTNEXT, rc = PC4500_readrid(ai, first ? RID_BSSLISTFIRST : RID_BSSLISTNEXT,
list, sizeof(*list)); list, sizeof(*list), 1);
list->len = le16_to_cpu(list->len); list->len = le16_to_cpu(list->len);
list->index = le16_to_cpu(list->index); list->index = le16_to_cpu(list->index);
...@@ -1071,7 +1100,7 @@ static int readBSSListRid(struct airo_info *ai, int first, ...@@ -1071,7 +1100,7 @@ static int readBSSListRid(struct airo_info *ai, int first,
static int readWepKeyRid(struct airo_info*ai, WepKeyRid *wkr, int temp) { static int readWepKeyRid(struct airo_info*ai, WepKeyRid *wkr, int temp) {
int rc = PC4500_readrid(ai, temp ? RID_WEP_TEMP : RID_WEP_PERM, int rc = PC4500_readrid(ai, temp ? RID_WEP_TEMP : RID_WEP_PERM,
wkr, sizeof(*wkr)); wkr, sizeof(*wkr), 1);
wkr->len = le16_to_cpu(wkr->len); wkr->len = le16_to_cpu(wkr->len);
wkr->kindex = le16_to_cpu(wkr->kindex); wkr->kindex = le16_to_cpu(wkr->kindex);
...@@ -1080,17 +1109,17 @@ static int readWepKeyRid(struct airo_info*ai, WepKeyRid *wkr, int temp) { ...@@ -1080,17 +1109,17 @@ static int readWepKeyRid(struct airo_info*ai, WepKeyRid *wkr, int temp) {
} }
/* In the writeXXXRid routines we copy the rids so that we don't screwup /* In the writeXXXRid routines we copy the rids so that we don't screwup
* the originals when we endian them... */ * the originals when we endian them... */
static int writeWepKeyRid(struct airo_info*ai, WepKeyRid *pwkr, int perm) { static int writeWepKeyRid(struct airo_info*ai, WepKeyRid *pwkr, int perm, int lock) {
int rc; int rc;
WepKeyRid wkr = *pwkr; WepKeyRid wkr = *pwkr;
wkr.len = cpu_to_le16(wkr.len); wkr.len = cpu_to_le16(wkr.len);
wkr.kindex = cpu_to_le16(wkr.kindex); wkr.kindex = cpu_to_le16(wkr.kindex);
wkr.klen = cpu_to_le16(wkr.klen); wkr.klen = cpu_to_le16(wkr.klen);
rc = PC4500_writerid(ai, RID_WEP_TEMP, &wkr, sizeof(wkr)); rc = PC4500_writerid(ai, RID_WEP_TEMP, &wkr, sizeof(wkr), lock);
if (rc!=SUCCESS) printk(KERN_ERR "airo: WEP_TEMP set %x\n", rc); if (rc!=SUCCESS) printk(KERN_ERR "airo: WEP_TEMP set %x\n", rc);
if (perm) { if (perm) {
rc = PC4500_writerid(ai, RID_WEP_PERM, &wkr, sizeof(wkr)); rc = PC4500_writerid(ai, RID_WEP_PERM, &wkr, sizeof(wkr), lock);
if (rc!=SUCCESS) { if (rc!=SUCCESS) {
printk(KERN_ERR "airo: WEP_PERM set %x\n", rc); printk(KERN_ERR "airo: WEP_PERM set %x\n", rc);
} }
...@@ -1100,7 +1129,7 @@ static int writeWepKeyRid(struct airo_info*ai, WepKeyRid *pwkr, int perm) { ...@@ -1100,7 +1129,7 @@ static int writeWepKeyRid(struct airo_info*ai, WepKeyRid *pwkr, int perm) {
static int readSsidRid(struct airo_info*ai, SsidRid *ssidr) { static int readSsidRid(struct airo_info*ai, SsidRid *ssidr) {
int i; int i;
int rc = PC4500_readrid(ai, RID_SSID, ssidr, sizeof(*ssidr)); int rc = PC4500_readrid(ai, RID_SSID, ssidr, sizeof(*ssidr), 1);
ssidr->len = le16_to_cpu(ssidr->len); ssidr->len = le16_to_cpu(ssidr->len);
for(i = 0; i < 3; i++) { for(i = 0; i < 3; i++) {
...@@ -1117,10 +1146,10 @@ static int writeSsidRid(struct airo_info*ai, SsidRid *pssidr) { ...@@ -1117,10 +1146,10 @@ static int writeSsidRid(struct airo_info*ai, SsidRid *pssidr) {
for(i = 0; i < 3; i++) { for(i = 0; i < 3; i++) {
ssidr.ssids[i].len = cpu_to_le16(ssidr.ssids[i].len); ssidr.ssids[i].len = cpu_to_le16(ssidr.ssids[i].len);
} }
rc = PC4500_writerid(ai, RID_SSID, &ssidr, sizeof(ssidr)); rc = PC4500_writerid(ai, RID_SSID, &ssidr, sizeof(ssidr), 1);
return rc; return rc;
} }
static int readConfigRid(struct airo_info*ai) { static int readConfigRid(struct airo_info*ai, int lock) {
int rc; int rc;
u16 *s; u16 *s;
ConfigRid cfg; ConfigRid cfg;
...@@ -1128,7 +1157,7 @@ static int readConfigRid(struct airo_info*ai) { ...@@ -1128,7 +1157,7 @@ static int readConfigRid(struct airo_info*ai) {
if (ai->config.len) if (ai->config.len)
return SUCCESS; return SUCCESS;
rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg)); rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg), lock);
if (rc != SUCCESS) if (rc != SUCCESS)
return rc; return rc;
...@@ -1157,7 +1186,7 @@ static inline void checkThrottle(struct airo_info *ai) { ...@@ -1157,7 +1186,7 @@ static inline void checkThrottle(struct airo_info *ai) {
} }
} }
} }
static int writeConfigRid(struct airo_info*ai) { static int writeConfigRid(struct airo_info*ai, int lock) {
u16 *s; u16 *s;
ConfigRid cfgr; ConfigRid cfgr;
...@@ -1184,33 +1213,34 @@ static int writeConfigRid(struct airo_info*ai) { ...@@ -1184,33 +1213,34 @@ static int writeConfigRid(struct airo_info*ai) {
for(s = &cfgr.arlThreshold; s <= &cfgr.autoWake; s++) for(s = &cfgr.arlThreshold; s <= &cfgr.autoWake; s++)
*s = cpu_to_le16(*s); *s = cpu_to_le16(*s);
return PC4500_writerid( ai, RID_CONFIG, &cfgr, sizeof(cfgr)); return PC4500_writerid( ai, RID_CONFIG, &cfgr, sizeof(cfgr), lock);
} }
static int readStatusRid(struct airo_info*ai, StatusRid *statr) { static int readStatusRid(struct airo_info*ai, StatusRid *statr) {
int rc = PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr)); int rc = PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr), 1);
u16 *s; u16 *s;
statr->len = le16_to_cpu(statr->len); statr->len = le16_to_cpu(statr->len);
for(s = &statr->mode; s <= &statr->SSIDlen; s++) *s = le16_to_cpu(*s); for(s = &statr->mode; s <= &statr->SSIDlen; s++) *s = le16_to_cpu(*s);
for(s = &statr->beaconPeriod; s <= &statr->_reserved[9]; s++) for(s = &statr->beaconPeriod; s <= &statr->shortPreamble; s++)
*s = le16_to_cpu(*s); *s = le16_to_cpu(*s);
statr->load = le16_to_cpu(statr->load);
statr->assocStatus = le16_to_cpu(statr->assocStatus);
return rc; return rc;
} }
static int readAPListRid(struct airo_info*ai, APListRid *aplr) { static int readAPListRid(struct airo_info*ai, APListRid *aplr) {
int rc = PC4500_readrid(ai, RID_APLIST, aplr, sizeof(*aplr)); int rc = PC4500_readrid(ai, RID_APLIST, aplr, sizeof(*aplr), 1);
aplr->len = le16_to_cpu(aplr->len); aplr->len = le16_to_cpu(aplr->len);
return rc; return rc;
} }
static int writeAPListRid(struct airo_info*ai, APListRid *aplr) { static int writeAPListRid(struct airo_info*ai, APListRid *aplr) {
int rc; int rc;
aplr->len = cpu_to_le16(aplr->len); aplr->len = cpu_to_le16(aplr->len);
rc = PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr)); rc = PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr), 1);
return rc; return rc;
} }
static int readCapabilityRid(struct airo_info*ai, CapabilityRid *capr) { static int readCapabilityRid(struct airo_info*ai, CapabilityRid *capr) {
int rc = PC4500_readrid(ai, RID_CAPABILITIES, capr, sizeof(*capr)); int rc = PC4500_readrid(ai, RID_CAPABILITIES, capr, sizeof(*capr), 1);
u16 *s; u16 *s;
capr->len = le16_to_cpu(capr->len); capr->len = le16_to_cpu(capr->len);
...@@ -1221,8 +1251,8 @@ static int readCapabilityRid(struct airo_info*ai, CapabilityRid *capr) { ...@@ -1221,8 +1251,8 @@ static int readCapabilityRid(struct airo_info*ai, CapabilityRid *capr) {
*s = le16_to_cpu(*s); *s = le16_to_cpu(*s);
return rc; return rc;
} }
static int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid) { static int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid, int lock) {
int rc = PC4500_readrid(ai, rid, sr, sizeof(*sr)); int rc = PC4500_readrid(ai, rid, sr, sizeof(*sr), lock);
u32 *i; u32 *i;
sr->len = le16_to_cpu(sr->len); sr->len = le16_to_cpu(sr->len);
...@@ -1242,8 +1272,8 @@ static int airo_open(struct net_device *dev) { ...@@ -1242,8 +1272,8 @@ static int airo_open(struct net_device *dev) {
* is open (to pipeline changes and speed-up card setup). If * is open (to pipeline changes and speed-up card setup). If
* those changes are not yet commited, do it now - Jean II */ * those changes are not yet commited, do it now - Jean II */
if(info->need_commit) { if(info->need_commit) {
disable_MAC(info); disable_MAC(info, 1);
writeConfigRid(info); writeConfigRid(info, 1);
} }
if (info->wifidev != dev) { if (info->wifidev != dev) {
...@@ -1251,7 +1281,7 @@ static int airo_open(struct net_device *dev) { ...@@ -1251,7 +1281,7 @@ static int airo_open(struct net_device *dev) {
info->flags &= ~FLAG_RADIO_DOWN; info->flags &= ~FLAG_RADIO_DOWN;
enable_interrupts(info); enable_interrupts(info);
} }
enable_MAC(info, &rsp); enable_MAC(info, &rsp, 1);
netif_start_queue(dev); netif_start_queue(dev);
return 0; return 0;
...@@ -1430,29 +1460,41 @@ static int airo_start_xmit11(struct sk_buff *skb, struct net_device *dev) { ...@@ -1430,29 +1460,41 @@ static int airo_start_xmit11(struct sk_buff *skb, struct net_device *dev) {
return 0; return 0;
} }
static void airo_read_stats(struct airo_info *ai) {
StatsRid stats_rid;
u32 *vals = stats_rid.vals;
if (down_trylock(&ai->sem) == 0) {
readStatsRid(ai, &stats_rid, RID_STATS, 0);
up(&ai->sem);
ai->stats.rx_packets = vals[43] + vals[44] + vals[45];
ai->stats.tx_packets = vals[39] + vals[40] + vals[41];
ai->stats.rx_bytes = vals[92];
ai->stats.tx_bytes = vals[91];
ai->stats.rx_errors = vals[0] + vals[2] + vals[3] + vals[4];
ai->stats.tx_errors = vals[42] + ai->stats.tx_fifo_errors;
ai->stats.multicast = vals[43];
ai->stats.collisions = vals[89];
/* detailed rx_errors: */
ai->stats.rx_length_errors = vals[3];
ai->stats.rx_crc_errors = vals[4];
ai->stats.rx_frame_errors = vals[2];
ai->stats.rx_fifo_errors = vals[0];
} else {
ai->stats_task.func = (void (*)(void *))airo_read_stats;
ai->stats_task.data = (void *)ai;
schedule_work(&ai->stats_task);
}
}
struct net_device_stats *airo_get_stats(struct net_device *dev) struct net_device_stats *airo_get_stats(struct net_device *dev)
{ {
struct airo_info *local = dev->priv; struct airo_info *local = dev->priv;
StatsRid stats_rid;
u32 *vals = stats_rid.vals;
/* Get stats out of the card */ /* Get stats out of the card if available */
readStatsRid(local, &stats_rid, RID_STATS); airo_read_stats(local);
local->stats.rx_packets = vals[43] + vals[44] + vals[45];
local->stats.tx_packets = vals[39] + vals[40] + vals[41];
local->stats.rx_bytes = vals[92];
local->stats.tx_bytes = vals[91];
local->stats.rx_errors = vals[0] + vals[2] + vals[3] + vals[4];
local->stats.tx_errors = vals[42] + local->stats.tx_fifo_errors;
local->stats.multicast = vals[43];
local->stats.collisions = vals[89];
/* detailed rx_errors: */
local->stats.rx_length_errors = vals[3];
local->stats.rx_crc_errors = vals[4];
local->stats.rx_frame_errors = vals[2];
local->stats.rx_fifo_errors = vals[0];
return &local->stats; return &local->stats;
} }
...@@ -1507,9 +1549,9 @@ static int airo_set_mac_address(struct net_device *dev, void *p) ...@@ -1507,9 +1549,9 @@ static int airo_set_mac_address(struct net_device *dev, void *p)
memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len); memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len);
ai->need_commit = 1; ai->need_commit = 1;
disable_MAC(ai); disable_MAC(ai, 1);
writeConfigRid (ai); writeConfigRid (ai, 1);
enable_MAC(ai, &rsp); enable_MAC(ai, &rsp, 1);
memcpy (ai->dev->dev_addr, addr->sa_data, dev->addr_len); memcpy (ai->dev->dev_addr, addr->sa_data, dev->addr_len);
if (ai->wifidev) if (ai->wifidev)
memcpy (ai->wifidev->dev_addr, addr->sa_data, dev->addr_len); memcpy (ai->wifidev->dev_addr, addr->sa_data, dev->addr_len);
...@@ -1538,7 +1580,7 @@ static int airo_close(struct net_device *dev) { ...@@ -1538,7 +1580,7 @@ static int airo_close(struct net_device *dev) {
* stack (i.e. the network stack won't try to broadcast * stack (i.e. the network stack won't try to broadcast
* anything on the interface and routes are gone. Jean II */ * anything on the interface and routes are gone. Jean II */
ai->flags |= FLAG_RADIO_DOWN; ai->flags |= FLAG_RADIO_DOWN;
disable_MAC(ai); disable_MAC(ai, 1);
#endif #endif
disable_interrupts( ai ); disable_interrupts( ai );
} }
...@@ -1762,6 +1804,9 @@ int reset_airo_card( struct net_device *dev ) { ...@@ -1762,6 +1804,9 @@ int reset_airo_card( struct net_device *dev ) {
int i; int i;
struct airo_info *ai = dev->priv; struct airo_info *ai = dev->priv;
if (down_interruptible(&ai->sem))
return -1;
waitbusy (ai); waitbusy (ai);
OUT4500(ai,COMMAND,CMD_SOFTRESET); OUT4500(ai,COMMAND,CMD_SOFTRESET);
set_current_state (TASK_UNINTERRUPTIBLE); set_current_state (TASK_UNINTERRUPTIBLE);
...@@ -1771,6 +1816,7 @@ int reset_airo_card( struct net_device *dev ) { ...@@ -1771,6 +1816,7 @@ int reset_airo_card( struct net_device *dev ) {
schedule_timeout (HZ/5); schedule_timeout (HZ/5);
if ( setup_card(ai, dev->dev_addr ) != SUCCESS ) { if ( setup_card(ai, dev->dev_addr ) != SUCCESS ) {
printk( KERN_ERR "airo: MAC could not be enabled\n" ); printk( KERN_ERR "airo: MAC could not be enabled\n" );
up(&ai->sem);
return -1; return -1;
} else { } else {
printk( KERN_INFO "airo: MAC enabled %s %x:%x:%x:%x:%x:%x\n", printk( KERN_INFO "airo: MAC enabled %s %x:%x:%x:%x:%x:%x\n",
...@@ -1788,6 +1834,7 @@ int reset_airo_card( struct net_device *dev ) { ...@@ -1788,6 +1834,7 @@ int reset_airo_card( struct net_device *dev ) {
} }
enable_interrupts( ai ); enable_interrupts( ai );
netif_wake_queue(dev); netif_wake_queue(dev);
up(&ai->sem);
return 0; return 0;
} }
...@@ -1800,9 +1847,7 @@ static void airo_send_event(struct net_device *dev) { ...@@ -1800,9 +1847,7 @@ static void airo_send_event(struct net_device *dev) {
StatusRid status_rid; StatusRid status_rid;
if (down_trylock(&ai->sem) == 0) { if (down_trylock(&ai->sem) == 0) {
__set_bit(FLAG_LOCKED, &ai->flags); PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid), 0);
PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid));
clear_bit(FLAG_LOCKED, &ai->flags);
up(&ai->sem); up(&ai->sem);
wrqu.data.length = 0; wrqu.data.length = 0;
wrqu.data.flags = 0; wrqu.data.flags = 0;
...@@ -1823,9 +1868,7 @@ static void airo_read_mic(struct airo_info *ai) { ...@@ -1823,9 +1868,7 @@ static void airo_read_mic(struct airo_info *ai) {
MICRid mic_rid; MICRid mic_rid;
if (down_trylock(&ai->sem) == 0) { if (down_trylock(&ai->sem) == 0) {
__set_bit(FLAG_LOCKED, &ai->flags); PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0);
PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid));
clear_bit(FLAG_LOCKED, &ai->flags);
up(&ai->sem); up(&ai->sem);
#ifdef MICSUPPORT #ifdef MICSUPPORT
micinit (ai, &mic_rid); micinit (ai, &mic_rid);
...@@ -1866,7 +1909,8 @@ static irqreturn_t airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs) ...@@ -1866,7 +1909,8 @@ static irqreturn_t airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs)
if ( status & EV_MIC ) { if ( status & EV_MIC ) {
OUT4500( apriv, EVACK, EV_MIC ); OUT4500( apriv, EVACK, EV_MIC );
airo_read_mic( apriv ); if (apriv->flags & FLAG_MIC_CAPABLE)
airo_read_mic( apriv );
} }
if ( status & EV_LINK ) { if ( status & EV_LINK ) {
#if WIRELESS_EXT > 13 #if WIRELESS_EXT > 13
...@@ -2057,6 +2101,28 @@ static irqreturn_t airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs) ...@@ -2057,6 +2101,28 @@ static irqreturn_t airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs)
} }
} }
if (len) { if (len) {
#if 0 && WIRELESS_EXT > 15
#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */
if (apriv->spy_data.spy_number > 0) {
char *sa;
struct iw_quality wstats;
/* Prepare spy data : addr + qual */
sa = (char*)buffer + ((apriv->flags & FLAG_802_11) ? 10 : 6);
if (!(apriv->flags & FLAG_802_11)) {
bap_setup (apriv, fid, 8, BAP0);
bap_read (apriv, (u16*)hdr.rssi, 2, BAP0);
}
wstats.qual = hdr.rssi[0];
if (apriv->rssi)
wstats.level = 0x100 - apriv->rssi[hdr.rssi[1]].rssidBm;
else
wstats.level = (hdr.rssi[1] + 321) / 2;
wstats.updated = 3;
/* Update spy records */
wireless_spy_update(dev, sa, &wstats);
}
#endif /* IW_WIRELESS_SPY */
#else /* WIRELESS_EXT > 15 */
#ifdef WIRELESS_SPY #ifdef WIRELESS_SPY
if (apriv->spy_number > 0) { if (apriv->spy_number > 0) {
int i; int i;
...@@ -2082,6 +2148,7 @@ static irqreturn_t airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs) ...@@ -2082,6 +2148,7 @@ static irqreturn_t airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs)
} }
} }
#endif /* WIRELESS_SPY */ #endif /* WIRELESS_SPY */
#endif /* WIRELESS_EXT > 15 */
OUT4500( apriv, EVACK, EV_RX); OUT4500( apriv, EVACK, EV_RX);
if (apriv->flags & FLAG_802_11) { if (apriv->flags & FLAG_802_11) {
...@@ -2113,17 +2180,17 @@ static irqreturn_t airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs) ...@@ -2113,17 +2180,17 @@ static irqreturn_t airo_interrupt ( int irq, void* dev_id, struct pt_regs *regs)
if ( ( apriv->fids[i] & 0xffff ) == fid ) { if ( ( apriv->fids[i] & 0xffff ) == fid ) {
len = apriv->fids[i] >> 16; len = apriv->fids[i] >> 16;
index = i; index = i;
/* Set up to be used again */
apriv->fids[i] &= 0xffff;
} }
} }
if (index != -1) { if (index != -1) {
netif_wake_queue(dev);
if (status & EV_TXEXC) if (status & EV_TXEXC)
get_tx_error(apriv, index); get_tx_error(apriv, index);
} OUT4500( apriv, EVACK, status & (EV_TX | EV_TXEXC));
OUT4500( apriv, EVACK, status & (EV_TX | EV_TXEXC)); /* Set up to be used again */
if (index==-1) { apriv->fids[index] &= 0xffff;
netif_wake_queue(dev);
} else {
OUT4500( apriv, EVACK, status & (EV_TX | EV_TXEXC));
printk( KERN_ERR "airo: Unallocated FID was used to xmit\n" ); printk( KERN_ERR "airo: Unallocated FID was used to xmit\n" );
} }
} }
...@@ -2169,7 +2236,7 @@ static u16 IN4500( struct airo_info *ai, u16 reg ) { ...@@ -2169,7 +2236,7 @@ static u16 IN4500( struct airo_info *ai, u16 reg ) {
return rc; return rc;
} }
static int enable_MAC( struct airo_info *ai, Resp *rsp ) { static int enable_MAC( struct airo_info *ai, Resp *rsp, int lock ) {
int rc; int rc;
Cmd cmd; Cmd cmd;
...@@ -2182,7 +2249,7 @@ static int enable_MAC( struct airo_info *ai, Resp *rsp ) { ...@@ -2182,7 +2249,7 @@ static int enable_MAC( struct airo_info *ai, Resp *rsp ) {
if (ai->flags & (FLAG_RADIO_OFF|FLAG_RADIO_DOWN)) return SUCCESS; if (ai->flags & (FLAG_RADIO_OFF|FLAG_RADIO_DOWN)) return SUCCESS;
memset(&cmd, 0, sizeof(cmd)); memset(&cmd, 0, sizeof(cmd));
cmd.cmd = MAC_ENABLE; cmd.cmd = MAC_ENABLE;
if (test_bit(FLAG_LOCKED, &ai->flags) != 0) if (!lock)
return issuecommand(ai, &cmd, rsp); return issuecommand(ai, &cmd, rsp);
if (down_interruptible(&ai->sem)) if (down_interruptible(&ai->sem))
...@@ -2192,13 +2259,13 @@ static int enable_MAC( struct airo_info *ai, Resp *rsp ) { ...@@ -2192,13 +2259,13 @@ static int enable_MAC( struct airo_info *ai, Resp *rsp ) {
return rc; return rc;
} }
static void disable_MAC( struct airo_info *ai ) { static void disable_MAC( struct airo_info *ai, int lock ) {
Cmd cmd; Cmd cmd;
Resp rsp; Resp rsp;
memset(&cmd, 0, sizeof(cmd)); memset(&cmd, 0, sizeof(cmd));
cmd.cmd = MAC_DISABLE; // disable in case already enabled cmd.cmd = MAC_DISABLE; // disable in case already enabled
if (test_bit(FLAG_LOCKED, &ai->flags) != 0) { if (!lock) {
issuecommand(ai, &cmd, &rsp); issuecommand(ai, &cmd, &rsp);
return; return;
} }
...@@ -2276,13 +2343,13 @@ static u16 setup_card(struct airo_info *ai, u8 *mac) ...@@ -2276,13 +2343,13 @@ static u16 setup_card(struct airo_info *ai, u8 *mac)
CapabilityRid cap_rid; CapabilityRid cap_rid;
// general configuration (read/modify/write) // general configuration (read/modify/write)
status = readConfigRid(ai); status = readConfigRid(ai, 1);
if ( status != SUCCESS ) return ERROR; if ( status != SUCCESS ) return ERROR;
status = readCapabilityRid(ai, &cap_rid); status = readCapabilityRid(ai, &cap_rid);
if ( status != SUCCESS ) return ERROR; if ( status != SUCCESS ) return ERROR;
status = PC4500_readrid(ai,RID_RSSI,&rssi_rid,sizeof(rssi_rid)); status = PC4500_readrid(ai,RID_RSSI,&rssi_rid,sizeof(rssi_rid),1);
if ( status == SUCCESS ) { if ( status == SUCCESS ) {
if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL) if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL)
memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512); memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512);
...@@ -2346,14 +2413,14 @@ static u16 setup_card(struct airo_info *ai, u8 *mac) ...@@ -2346,14 +2413,14 @@ static u16 setup_card(struct airo_info *ai, u8 *mac)
} }
} }
status = writeConfigRid(ai); status = writeConfigRid(ai, 1);
if ( status != SUCCESS ) return ERROR; if ( status != SUCCESS ) return ERROR;
/* Set up the SSID list */ /* Set up the SSID list */
status = writeSsidRid(ai, &mySsid); status = writeSsidRid(ai, &mySsid);
if ( status != SUCCESS ) return ERROR; if ( status != SUCCESS ) return ERROR;
status = enable_MAC(ai, &rsp); status = enable_MAC(ai, &rsp, 1);
if ( status != SUCCESS || (rsp.status & 0xFF00) != 0) { if ( status != SUCCESS || (rsp.status & 0xFF00) != 0) {
printk( KERN_ERR "airo: Bad MAC enable reason = %x, rid = %x, offset = %d\n", rsp.rsp0, rsp.rsp1, rsp.rsp2 ); printk( KERN_ERR "airo: Bad MAC enable reason = %x, rid = %x, offset = %d\n", rsp.rsp0, rsp.rsp1, rsp.rsp2 );
return ERROR; return ERROR;
...@@ -2568,13 +2635,12 @@ static int PC4500_accessrid(struct airo_info *ai, u16 rid, u16 accmd) ...@@ -2568,13 +2635,12 @@ static int PC4500_accessrid(struct airo_info *ai, u16 rid, u16 accmd)
/* Note, that we are using BAP1 which is also used by transmit, so /* Note, that we are using BAP1 which is also used by transmit, so
* we must get a lock. */ * we must get a lock. */
static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len) static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len, int lock)
{ {
u16 status, dolock = 0; u16 status;
int rc = SUCCESS; int rc = SUCCESS;
if (test_bit(FLAG_LOCKED, &ai->flags) == 0) { if (lock) {
dolock = 1;
if (down_interruptible(&ai->sem)) if (down_interruptible(&ai->sem))
return ERROR; return ERROR;
} }
...@@ -2602,7 +2668,7 @@ static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len) ...@@ -2602,7 +2668,7 @@ static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len)
// read remainder of the rid // read remainder of the rid
rc = bap_read(ai, ((u16*)pBuf)+1, len, BAP1); rc = bap_read(ai, ((u16*)pBuf)+1, len, BAP1);
done: done:
if (dolock) if (lock)
up(&ai->sem); up(&ai->sem);
return rc; return rc;
} }
...@@ -2610,13 +2676,14 @@ static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len) ...@@ -2610,13 +2676,14 @@ static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len)
/* Note, that we are using BAP1 which is also used by transmit, so /* Note, that we are using BAP1 which is also used by transmit, so
* make sure this isnt called when a transmit is happening */ * make sure this isnt called when a transmit is happening */
static int PC4500_writerid(struct airo_info *ai, u16 rid, static int PC4500_writerid(struct airo_info *ai, u16 rid,
const void *pBuf, int len) const void *pBuf, int len, int lock)
{ {
u16 status, dolock = 0; u16 status;
int rc = SUCCESS; int rc = SUCCESS;
if (test_bit(FLAG_LOCKED, &ai->flags) == 0) { *(u16*)pBuf = cpu_to_le16((u16)len);
dolock = 1;
if (lock) {
if (down_interruptible(&ai->sem)) if (down_interruptible(&ai->sem))
return ERROR; return ERROR;
} }
...@@ -2634,7 +2701,7 @@ static int PC4500_writerid(struct airo_info *ai, u16 rid, ...@@ -2634,7 +2701,7 @@ static int PC4500_writerid(struct airo_info *ai, u16 rid,
// ---now commit the rid data // ---now commit the rid data
rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS); rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS);
done: done:
if (dolock) if (lock)
up(&ai->sem); up(&ai->sem);
return rc; return rc;
} }
...@@ -2653,11 +2720,11 @@ static u16 transmit_allocate(struct airo_info *ai, int lenPayload, int raw) ...@@ -2653,11 +2720,11 @@ static u16 transmit_allocate(struct airo_info *ai, int lenPayload, int raw)
if (down_interruptible(&ai->sem)) if (down_interruptible(&ai->sem))
return ERROR; return ERROR;
if (issuecommand(ai, &cmd, &rsp) != SUCCESS) { if (issuecommand(ai, &cmd, &rsp) != SUCCESS) {
txFid = 0; txFid = ERROR;
goto done; goto done;
} }
if ( (rsp.status & 0xFF00) != 0) { if ( (rsp.status & 0xFF00) != 0) {
txFid = 0; txFid = ERROR;
goto done; goto done;
} }
/* wait for the allocate event/indication /* wait for the allocate event/indication
...@@ -2704,7 +2771,7 @@ static int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket) ...@@ -2704,7 +2771,7 @@ static int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket)
len >>= 16; len >>= 16;
if (len < ETH_ALEN * 2) { if (len <= ETH_ALEN * 2) {
printk( KERN_WARNING "Short packet %d\n", len ); printk( KERN_WARNING "Short packet %d\n", len );
return ERROR; return ERROR;
} }
...@@ -3160,7 +3227,7 @@ static int proc_stats_rid_open( struct inode *inode, ...@@ -3160,7 +3227,7 @@ static int proc_stats_rid_open( struct inode *inode,
return -ENOMEM; return -ENOMEM;
} }
readStatsRid(apriv, &stats, rid); readStatsRid(apriv, &stats, rid, 1);
j = 0; j = 0;
for(i=0; statsLabels[i]!=(char *)-1 && for(i=0; statsLabels[i]!=(char *)-1 &&
...@@ -3195,18 +3262,21 @@ static int get_dec_u16( char *buffer, int *start, int limit ) { ...@@ -3195,18 +3262,21 @@ static int get_dec_u16( char *buffer, int *start, int limit ) {
return value; return value;
} }
static int airo_config_commit(struct net_device *dev,
struct iw_request_info *info, void *zwrq,
char *extra);
static void proc_config_on_close( struct inode *inode, struct file *file ) { static void proc_config_on_close( struct inode *inode, struct file *file ) {
struct proc_data *data = file->private_data; struct proc_data *data = file->private_data;
struct proc_dir_entry *dp = PDE(inode); struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data; struct net_device *dev = dp->data;
struct airo_info *ai = dev->priv; struct airo_info *ai = dev->priv;
Resp rsp;
char *line; char *line;
int need_reset = 0;
if ( !data->writelen ) return; if ( !data->writelen ) return;
readConfigRid(ai); readConfigRid(ai, 1);
ai->need_commit = 1;
line = data->wbuffer; line = data->wbuffer;
while( line[0] ) { while( line[0] ) {
...@@ -3214,7 +3284,7 @@ static void proc_config_on_close( struct inode *inode, struct file *file ) { ...@@ -3214,7 +3284,7 @@ static void proc_config_on_close( struct inode *inode, struct file *file ) {
if ( !strncmp( line, "Mode: ", 6 ) ) { if ( !strncmp( line, "Mode: ", 6 ) ) {
line += 6; line += 6;
if ((ai->config.rmode & 0xff) >= RXMODE_RFMON) if ((ai->config.rmode & 0xff) >= RXMODE_RFMON)
need_reset = 1; ai->need_commit = 2;
ai->config.rmode &= 0xfe00; ai->config.rmode &= 0xfe00;
ai->flags &= ~FLAG_802_11; ai->flags &= ~FLAG_802_11;
ai->config.opmode &= 0xFF00; ai->config.opmode &= 0xFF00;
...@@ -3392,22 +3462,7 @@ static void proc_config_on_close( struct inode *inode, struct file *file ) { ...@@ -3392,22 +3462,7 @@ static void proc_config_on_close( struct inode *inode, struct file *file ) {
while( line[0] && line[0] != '\n' ) line++; while( line[0] && line[0] != '\n' ) line++;
if ( line[0] ) line++; if ( line[0] ) line++;
} }
disable_MAC(ai); airo_config_commit(dev, NULL, NULL, NULL);
if (need_reset) {
APListRid APList_rid;
SsidRid SSID_rid;
readAPListRid(ai, &APList_rid);
readSsidRid(ai, &SSID_rid);
reset_airo_card(dev);
disable_MAC(ai);
writeSsidRid(ai, &SSID_rid);
writeAPListRid(ai, &APList_rid);
}
writeConfigRid(ai);
enable_MAC(ai, &rsp);
if (need_reset)
airo_set_promisc(ai);
} }
static char *get_rmode(u16 mode) { static char *get_rmode(u16 mode) {
...@@ -3443,7 +3498,7 @@ static int proc_config_open( struct inode *inode, struct file *file ) { ...@@ -3443,7 +3498,7 @@ static int proc_config_open( struct inode *inode, struct file *file ) {
data->maxwritelen = 2048; data->maxwritelen = 2048;
data->on_close = proc_config_on_close; data->on_close = proc_config_on_close;
readConfigRid(ai); readConfigRid(ai, 1);
i = sprintf( data->rbuffer, i = sprintf( data->rbuffer,
"Mode: %s\n" "Mode: %s\n"
...@@ -3535,9 +3590,9 @@ static void proc_SSID_on_close( struct inode *inode, struct file *file ) { ...@@ -3535,9 +3590,9 @@ static void proc_SSID_on_close( struct inode *inode, struct file *file ) {
offset < data->writelen ) offset++; offset < data->writelen ) offset++;
offset++; offset++;
} }
disable_MAC(ai); disable_MAC(ai, 1);
writeSsidRid(ai, &SSID_rid); writeSsidRid(ai, &SSID_rid);
enable_MAC(ai, &rsp); enable_MAC(ai, &rsp, 1);
} }
inline static u8 hexVal(char c) { inline static u8 hexVal(char c) {
...@@ -3576,20 +3631,20 @@ static void proc_APList_on_close( struct inode *inode, struct file *file ) { ...@@ -3576,20 +3631,20 @@ static void proc_APList_on_close( struct inode *inode, struct file *file ) {
} }
} }
} }
disable_MAC(ai); disable_MAC(ai, 1);
writeAPListRid(ai, &APList_rid); writeAPListRid(ai, &APList_rid);
enable_MAC(ai, &rsp); enable_MAC(ai, &rsp, 1);
} }
/* This function wraps PC4500_writerid with a MAC disable */ /* This function wraps PC4500_writerid with a MAC disable */
static int do_writerid( struct airo_info *ai, u16 rid, const void *rid_data, static int do_writerid( struct airo_info *ai, u16 rid, const void *rid_data,
int len ) { int len, int dummy ) {
int rc; int rc;
Resp rsp; Resp rsp;
disable_MAC(ai); disable_MAC(ai, 1);
rc = PC4500_writerid(ai, rid, rid_data, len); rc = PC4500_writerid(ai, rid, rid_data, len, 1);
enable_MAC(ai, &rsp); enable_MAC(ai, &rsp, 1);
return rc; return rc;
} }
...@@ -3617,7 +3672,7 @@ static int get_wep_key(struct airo_info *ai, u16 index) { ...@@ -3617,7 +3672,7 @@ static int get_wep_key(struct airo_info *ai, u16 index) {
} }
static int set_wep_key(struct airo_info *ai, u16 index, static int set_wep_key(struct airo_info *ai, u16 index,
const char *key, u16 keylen, int perm ) { const char *key, u16 keylen, int perm, int lock ) {
static const unsigned char macaddr[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 }; static const unsigned char macaddr[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 };
WepKeyRid wkr; WepKeyRid wkr;
...@@ -3639,7 +3694,7 @@ static int set_wep_key(struct airo_info *ai, u16 index, ...@@ -3639,7 +3694,7 @@ static int set_wep_key(struct airo_info *ai, u16 index,
printk(KERN_INFO "Setting key %d\n", index); printk(KERN_INFO "Setting key %d\n", index);
} }
writeWepKeyRid(ai, &wkr, perm); writeWepKeyRid(ai, &wkr, perm, lock);
return 0; return 0;
} }
...@@ -3662,7 +3717,7 @@ static void proc_wepkey_on_close( struct inode *inode, struct file *file ) { ...@@ -3662,7 +3717,7 @@ static void proc_wepkey_on_close( struct inode *inode, struct file *file ) {
(data->wbuffer[1] == ' ' || data->wbuffer[1] == '\n')) { (data->wbuffer[1] == ' ' || data->wbuffer[1] == '\n')) {
index = data->wbuffer[0] - '0'; index = data->wbuffer[0] - '0';
if (data->wbuffer[1] == '\n') { if (data->wbuffer[1] == '\n') {
set_wep_key(ai, index, 0, 0, 1); set_wep_key(ai, index, 0, 0, 1, 1);
return; return;
} }
j = 2; j = 2;
...@@ -3681,7 +3736,7 @@ static void proc_wepkey_on_close( struct inode *inode, struct file *file ) { ...@@ -3681,7 +3736,7 @@ static void proc_wepkey_on_close( struct inode *inode, struct file *file ) {
break; break;
} }
} }
set_wep_key(ai, index, key, i/3, 1); set_wep_key(ai, index, key, i/3, 1, 1);
} }
static int proc_wepkey_open( struct inode *inode, struct file *file ) { static int proc_wepkey_open( struct inode *inode, struct file *file ) {
...@@ -3928,10 +3983,9 @@ static void timer_func( u_long data ) { ...@@ -3928,10 +3983,9 @@ static void timer_func( u_long data ) {
add_timer(&apriv->timer); add_timer(&apriv->timer);
return; return;
} }
__set_bit(FLAG_LOCKED, &apriv->flags);
readConfigRid(apriv); readConfigRid(apriv, 0);
disable_MAC(apriv); disable_MAC(apriv, 0);
switch(apriv->config.authType) { switch(apriv->config.authType) {
case AUTH_ENCRYPT: case AUTH_ENCRYPT:
/* So drop to OPEN */ /* So drop to OPEN */
...@@ -3939,13 +3993,13 @@ static void timer_func( u_long data ) { ...@@ -3939,13 +3993,13 @@ static void timer_func( u_long data ) {
break; break;
case AUTH_SHAREDKEY: case AUTH_SHAREDKEY:
if (apriv->keyindex < auto_wep) { if (apriv->keyindex < auto_wep) {
set_wep_key(apriv, apriv->keyindex, 0, 0, 0); set_wep_key(apriv, apriv->keyindex, 0, 0, 0, 0);
apriv->config.authType = AUTH_SHAREDKEY; apriv->config.authType = AUTH_SHAREDKEY;
apriv->keyindex++; apriv->keyindex++;
} else { } else {
/* Drop to ENCRYPT */ /* Drop to ENCRYPT */
apriv->keyindex = 0; apriv->keyindex = 0;
set_wep_key(apriv, apriv->defindex, 0, 0, 0); set_wep_key(apriv, apriv->defindex, 0, 0, 0, 0);
apriv->config.authType = AUTH_ENCRYPT; apriv->config.authType = AUTH_ENCRYPT;
} }
break; break;
...@@ -3953,9 +4007,8 @@ static void timer_func( u_long data ) { ...@@ -3953,9 +4007,8 @@ static void timer_func( u_long data ) {
apriv->config.authType = AUTH_SHAREDKEY; apriv->config.authType = AUTH_SHAREDKEY;
} }
apriv->need_commit = 1; apriv->need_commit = 1;
writeConfigRid(apriv); writeConfigRid(apriv, 0);
enable_MAC(apriv, &rsp); enable_MAC(apriv, &rsp, 0);
clear_bit(FLAG_LOCKED, &apriv->flags);
up(&apriv->sem); up(&apriv->sem);
/* Schedule check to see if the change worked */ /* Schedule check to see if the change worked */
...@@ -4135,9 +4188,11 @@ static int airo_get_freq(struct net_device *dev, ...@@ -4135,9 +4188,11 @@ static int airo_get_freq(struct net_device *dev,
struct airo_info *local = dev->priv; struct airo_info *local = dev->priv;
StatusRid status_rid; /* Card status info */ StatusRid status_rid; /* Card status info */
readStatusRid(local, &status_rid); if ((local->config.opmode & 0xFF) == MODE_STA_ESS)
status_rid.channel = local->config.channelSet;
else
readStatusRid(local, &status_rid);
/* Will return zero in infrastructure mode */
#ifdef WEXT_USECHANNELS #ifdef WEXT_USECHANNELS
fwrq->m = ((int)status_rid.channel) + 1; fwrq->m = ((int)status_rid.channel) + 1;
fwrq->e = 0; fwrq->e = 0;
...@@ -4191,9 +4246,9 @@ static int airo_set_essid(struct net_device *dev, ...@@ -4191,9 +4246,9 @@ static int airo_set_essid(struct net_device *dev,
SSID_rid.ssids[index].len = dwrq->length - 1; SSID_rid.ssids[index].len = dwrq->length - 1;
} }
/* Write it to the card */ /* Write it to the card */
disable_MAC(local); disable_MAC(local, 1);
writeSsidRid(local, &SSID_rid); writeSsidRid(local, &SSID_rid);
enable_MAC(local, &rsp); enable_MAC(local, &rsp, 1);
return 0; return 0;
} }
...@@ -4255,9 +4310,9 @@ static int airo_set_wap(struct net_device *dev, ...@@ -4255,9 +4310,9 @@ static int airo_set_wap(struct net_device *dev,
memset(&APList_rid, 0, sizeof(APList_rid)); memset(&APList_rid, 0, sizeof(APList_rid));
APList_rid.len = sizeof(APList_rid); APList_rid.len = sizeof(APList_rid);
memcpy(APList_rid.ap[0], awrq->sa_data, ETH_ALEN); memcpy(APList_rid.ap[0], awrq->sa_data, ETH_ALEN);
disable_MAC(local); disable_MAC(local, 1);
writeAPListRid(local, &APList_rid); writeAPListRid(local, &APList_rid);
enable_MAC(local, &rsp); enable_MAC(local, &rsp, 1);
} }
return 0; return 0;
} }
...@@ -4506,28 +4561,47 @@ static int airo_set_mode(struct net_device *dev, ...@@ -4506,28 +4561,47 @@ static int airo_set_mode(struct net_device *dev,
char *extra) char *extra)
{ {
struct airo_info *local = dev->priv; struct airo_info *local = dev->priv;
int commit = 1;
if ((local->config.rmode & 0xff) >= RXMODE_RFMON)
commit = 2;
switch(*uwrq) { switch(*uwrq) {
case IW_MODE_ADHOC: case IW_MODE_ADHOC:
local->config.opmode &= 0xFF00; local->config.opmode &= 0xFF00;
local->config.opmode |= MODE_STA_IBSS; local->config.opmode |= MODE_STA_IBSS;
local->config.rmode &= 0xfe00;
local->flags &= ~FLAG_802_11;
break; break;
case IW_MODE_INFRA: case IW_MODE_INFRA:
local->config.opmode &= 0xFF00; local->config.opmode &= 0xFF00;
local->config.opmode |= MODE_STA_ESS; local->config.opmode |= MODE_STA_ESS;
local->config.rmode &= 0xfe00;
local->flags &= ~FLAG_802_11;
break; break;
case IW_MODE_MASTER: case IW_MODE_MASTER:
local->config.opmode &= 0xFF00; local->config.opmode &= 0xFF00;
local->config.opmode |= MODE_AP; local->config.opmode |= MODE_AP;
local->config.rmode &= 0xfe00;
local->flags &= ~FLAG_802_11;
break; break;
case IW_MODE_REPEAT: case IW_MODE_REPEAT:
local->config.opmode &= 0xFF00; local->config.opmode &= 0xFF00;
local->config.opmode |= MODE_AP_RPTR; local->config.opmode |= MODE_AP_RPTR;
local->config.rmode &= 0xfe00;
local->flags &= ~FLAG_802_11;
break;
case IW_MODE_MONITOR:
local->config.opmode &= 0xFF00;
local->config.opmode |= MODE_STA_ESS;
local->config.rmode &= 0xfe00;
local->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
local->flags |= FLAG_802_11;
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
local->need_commit = 1; local->need_commit = commit;
return -EINPROGRESS; /* Call commit handler */ return -EINPROGRESS; /* Call commit handler */
} }
...@@ -4613,7 +4687,7 @@ static int airo_set_encode(struct net_device *dev, ...@@ -4613,7 +4687,7 @@ static int airo_set_encode(struct net_device *dev,
/* Copy the key in the driver */ /* Copy the key in the driver */
memcpy(key.key, extra, dwrq->length); memcpy(key.key, extra, dwrq->length);
/* Send the key to the card */ /* Send the key to the card */
set_wep_key(local, index, key.key, key.len, 1); set_wep_key(local, index, key.key, key.len, 1, 1);
} }
/* WE specify that if a valid key is set, encryption /* WE specify that if a valid key is set, encryption
* should be enabled (user may turn it off later) * should be enabled (user may turn it off later)
...@@ -4627,7 +4701,7 @@ static int airo_set_encode(struct net_device *dev, ...@@ -4627,7 +4701,7 @@ static int airo_set_encode(struct net_device *dev,
/* Do we want to just set the transmit key index ? */ /* Do we want to just set the transmit key index ? */
int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
if ((index>=0) && (index<(cap_rid.softCap&0x80)?4:1)) { if ((index>=0) && (index<(cap_rid.softCap&0x80)?4:1)) {
set_wep_key(local, index, 0, 0, 1); set_wep_key(local, index, 0, 0, 1, 1);
} else } else
/* Don't complain if only change the mode */ /* Don't complain if only change the mode */
if(!dwrq->flags & IW_ENCODE_MODE) { if(!dwrq->flags & IW_ENCODE_MODE) {
...@@ -5272,7 +5346,7 @@ static int airo_get_scan(struct net_device *dev, ...@@ -5272,7 +5346,7 @@ static int airo_get_scan(struct net_device *dev,
* consequences are begnign. So I don't bother fixing it - Javier */ * consequences are begnign. So I don't bother fixing it - Javier */
/* Try to read the first entry of the scan result */ /* Try to read the first entry of the scan result */
rc = PC4500_readrid(ai, RID_BSSLISTFIRST, &BSSList, sizeof(BSSList)); rc = PC4500_readrid(ai, RID_BSSLISTFIRST, &BSSList, sizeof(BSSList), 1);
if((rc) || (BSSList.index == 0xffff)) { if((rc) || (BSSList.index == 0xffff)) {
/* Client error, no scan results... /* Client error, no scan results...
* The caller need to restart the scan. */ * The caller need to restart the scan. */
...@@ -5288,7 +5362,7 @@ static int airo_get_scan(struct net_device *dev, ...@@ -5288,7 +5362,7 @@ static int airo_get_scan(struct net_device *dev,
/* Read next entry */ /* Read next entry */
rc = PC4500_readrid(ai, RID_BSSLISTNEXT, rc = PC4500_readrid(ai, RID_BSSLISTNEXT,
&BSSList, sizeof(BSSList)); &BSSList, sizeof(BSSList), 1);
} }
/* Length of data */ /* Length of data */
dwrq->length = (current_ev - extra); dwrq->length = (current_ev - extra);
...@@ -5298,6 +5372,7 @@ static int airo_get_scan(struct net_device *dev, ...@@ -5298,6 +5372,7 @@ static int airo_get_scan(struct net_device *dev,
} }
#endif /* WIRELESS_EXT > 13 */ #endif /* WIRELESS_EXT > 13 */
#if WIRELESS_EXT <= 15
#ifdef WIRELESS_SPY #ifdef WIRELESS_SPY
/*------------------------------------------------------------------*/ /*------------------------------------------------------------------*/
/* /*
...@@ -5360,6 +5435,7 @@ static int airo_get_spy(struct net_device *dev, ...@@ -5360,6 +5435,7 @@ static int airo_get_spy(struct net_device *dev,
return 0; return 0;
} }
#endif /* WIRELESS_SPY */ #endif /* WIRELESS_SPY */
#endif /* WIRELESS_EXT <= 15 */
/*------------------------------------------------------------------*/ /*------------------------------------------------------------------*/
/* /*
...@@ -5378,9 +5454,22 @@ static int airo_config_commit(struct net_device *dev, ...@@ -5378,9 +5454,22 @@ static int airo_config_commit(struct net_device *dev,
/* Some of the "SET" function may have modified some of the /* Some of the "SET" function may have modified some of the
* parameters. It's now time to commit them in the card */ * parameters. It's now time to commit them in the card */
disable_MAC(local); disable_MAC(local, 1);
writeConfigRid(local); if (local->need_commit > 1) {
enable_MAC(local, &rsp); APListRid APList_rid;
SsidRid SSID_rid;
readAPListRid(local, &APList_rid);
readSsidRid(local, &SSID_rid);
reset_airo_card(dev);
disable_MAC(local, 1);
writeSsidRid(local, &SSID_rid);
writeAPListRid(local, &APList_rid);
}
writeConfigRid(local, 1);
enable_MAC(local, &rsp, 1);
if (local->need_commit > 1)
airo_set_promisc(local);
return 0; return 0;
} }
...@@ -5417,6 +5506,12 @@ static const iw_handler airo_handler[] = ...@@ -5417,6 +5506,12 @@ static const iw_handler airo_handler[] =
(iw_handler) NULL, /* SIOCGIWPRIV */ (iw_handler) NULL, /* SIOCGIWPRIV */
(iw_handler) NULL, /* SIOCSIWSTATS */ (iw_handler) NULL, /* SIOCSIWSTATS */
(iw_handler) NULL, /* SIOCGIWSTATS */ (iw_handler) NULL, /* SIOCGIWSTATS */
#if WIRELESS_EXT > 15
iw_handler_set_spy, /* SIOCSIWSPY */
iw_handler_get_spy, /* SIOCGIWSPY */
iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
#else /* WIRELESS_EXT > 15 */
#ifdef WIRELESS_SPY #ifdef WIRELESS_SPY
(iw_handler) airo_set_spy, /* SIOCSIWSPY */ (iw_handler) airo_set_spy, /* SIOCSIWSPY */
(iw_handler) airo_get_spy, /* SIOCGIWSPY */ (iw_handler) airo_get_spy, /* SIOCGIWSPY */
...@@ -5426,6 +5521,7 @@ static const iw_handler airo_handler[] = ...@@ -5426,6 +5521,7 @@ static const iw_handler airo_handler[] =
#endif /* WIRELESS_SPY */ #endif /* WIRELESS_SPY */
(iw_handler) NULL, /* -- hole -- */ (iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */ (iw_handler) NULL, /* -- hole -- */
#endif /* WIRELESS_EXT > 15 */
(iw_handler) airo_set_wap, /* SIOCSIWAP */ (iw_handler) airo_set_wap, /* SIOCSIWAP */
(iw_handler) airo_get_wap, /* SIOCGIWAP */ (iw_handler) airo_get_wap, /* SIOCGIWAP */
(iw_handler) NULL, /* -- hole -- */ (iw_handler) NULL, /* -- hole -- */
...@@ -5479,6 +5575,11 @@ static const struct iw_handler_def airo_handler_def = ...@@ -5479,6 +5575,11 @@ static const struct iw_handler_def airo_handler_def =
.standard = (iw_handler *) airo_handler, .standard = (iw_handler *) airo_handler,
.private = (iw_handler *) airo_private_handler, .private = (iw_handler *) airo_private_handler,
.private_args = (struct iw_priv_args *) airo_private_args, .private_args = (struct iw_priv_args *) airo_private_args,
#if 0 && WIRELESS_EXT > 15
.spy_offset = ((void *) (&((struct airo_info *) NULL)->spy_data) -
(void *) NULL),
#endif /* WIRELESS_EXT > 15 */
}; };
#endif /* WIRELESS_EXT > 12 */ #endif /* WIRELESS_EXT > 12 */
...@@ -5864,7 +5965,7 @@ struct iw_statistics *airo_get_wireless_stats(struct net_device *dev) ...@@ -5864,7 +5965,7 @@ struct iw_statistics *airo_get_wireless_stats(struct net_device *dev)
/* Get stats out of the card */ /* Get stats out of the card */
readStatusRid(local, &status_rid); readStatusRid(local, &status_rid);
readStatsRid(local, &stats_rid, RID_STATS); readStatsRid(local, &stats_rid, RID_STATS, 1);
/* The status */ /* The status */
local->wstats.status = status_rid.mode; local->wstats.status = status_rid.mode;
...@@ -5902,18 +6003,14 @@ static int readrids(struct net_device *dev, aironet_ioctl *comp) { ...@@ -5902,18 +6003,14 @@ static int readrids(struct net_device *dev, aironet_ioctl *comp) {
unsigned short ridcode; unsigned short ridcode;
unsigned char *iobuf; unsigned char *iobuf;
struct airo_info *ai = dev->priv; struct airo_info *ai = dev->priv;
int ret = 0;
if (ai->flags & FLAG_FLASHING) if (ai->flags & FLAG_FLASHING)
return -EIO; return -EIO;
iobuf = kmalloc(RIDS_SIZE, GFP_KERNEL);
if (!iobuf)
return -ENOMEM;
switch(comp->command) switch(comp->command)
{ {
case AIROGCAP: ridcode = RID_CAPABILITIES; break; case AIROGCAP: ridcode = RID_CAPABILITIES; break;
case AIROGCFG: writeConfigRid (ai); case AIROGCFG: writeConfigRid (ai, 1);
ridcode = RID_CONFIG; break; ridcode = RID_CONFIG; break;
case AIROGSLIST: ridcode = RID_SSID; break; case AIROGSLIST: ridcode = RID_SSID; break;
case AIROGVLIST: ridcode = RID_APLIST; break; case AIROGVLIST: ridcode = RID_APLIST; break;
...@@ -5921,17 +6018,13 @@ static int readrids(struct net_device *dev, aironet_ioctl *comp) { ...@@ -5921,17 +6018,13 @@ static int readrids(struct net_device *dev, aironet_ioctl *comp) {
case AIROGEHTENC: ridcode = RID_ETHERENCAP; break; case AIROGEHTENC: ridcode = RID_ETHERENCAP; break;
case AIROGWEPKTMP: ridcode = RID_WEP_TEMP; case AIROGWEPKTMP: ridcode = RID_WEP_TEMP;
/* Only super-user can read WEP keys */ /* Only super-user can read WEP keys */
if (!capable(CAP_NET_ADMIN)) { if (!capable(CAP_NET_ADMIN))
ret = -EPERM; return -EPERM;
goto rr_free;
}
break; break;
case AIROGWEPKNV: ridcode = RID_WEP_PERM; case AIROGWEPKNV: ridcode = RID_WEP_PERM;
/* Only super-user can read WEP keys */ /* Only super-user can read WEP keys */
if (!capable(CAP_NET_ADMIN)) { if (!capable(CAP_NET_ADMIN))
ret = -EPERM; return -EPERM;
goto rr_free;
}
break; break;
case AIROGSTAT: ridcode = RID_STATUS; break; case AIROGSTAT: ridcode = RID_STATUS; break;
case AIROGSTATSD32: ridcode = RID_STATSDELTA; break; case AIROGSTATSD32: ridcode = RID_STATSDELTA; break;
...@@ -5939,25 +6032,29 @@ static int readrids(struct net_device *dev, aironet_ioctl *comp) { ...@@ -5939,25 +6032,29 @@ static int readrids(struct net_device *dev, aironet_ioctl *comp) {
case AIROGMICSTATS: case AIROGMICSTATS:
if (copy_to_user(comp->data, &ai->micstats, if (copy_to_user(comp->data, &ai->micstats,
min((int)comp->len,(int)sizeof(ai->micstats)))) min((int)comp->len,(int)sizeof(ai->micstats))))
ret = -EFAULT; return -EFAULT;
goto rr_free; return 0;
default: default:
ret = -EINVAL; return -EINVAL;
goto rr_free; break;
} }
PC4500_readrid(ai,ridcode,iobuf,RIDS_SIZE); if ((iobuf = kmalloc(RIDS_SIZE, GFP_KERNEL)) == NULL)
return -ENOMEM;
PC4500_readrid(ai,ridcode,iobuf,RIDS_SIZE, 1);
/* get the count of bytes in the rid docs say 1st 2 bytes is it. /* get the count of bytes in the rid docs say 1st 2 bytes is it.
* then return it to the user * then return it to the user
* 9/22/2000 Honor user given length * 9/22/2000 Honor user given length
*/ */
if (copy_to_user(comp->data, iobuf, if (copy_to_user(comp->data, iobuf,
min((int)comp->len, (int)RIDS_SIZE))) min((int)comp->len, (int)RIDS_SIZE))) {
ret = -EFAULT; kfree (iobuf);
rr_free: return -EFAULT;
kfree(iobuf); }
return ret; kfree (iobuf);
return 0;
} }
/* /*
...@@ -5968,9 +6065,8 @@ static int writerids(struct net_device *dev, aironet_ioctl *comp) { ...@@ -5968,9 +6065,8 @@ static int writerids(struct net_device *dev, aironet_ioctl *comp) {
struct airo_info *ai = dev->priv; struct airo_info *ai = dev->priv;
int ridcode, enabled; int ridcode, enabled;
Resp rsp; Resp rsp;
static int (* writer)(struct airo_info *, u16 rid, const void *, int); static int (* writer)(struct airo_info *, u16 rid, const void *, int, int);
unsigned char *iobuf; unsigned char *iobuf;
int ret = 0;
/* Only super-user can write RIDs */ /* Only super-user can write RIDs */
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN))
...@@ -5979,10 +6075,6 @@ static int writerids(struct net_device *dev, aironet_ioctl *comp) { ...@@ -5979,10 +6075,6 @@ static int writerids(struct net_device *dev, aironet_ioctl *comp) {
if (ai->flags & FLAG_FLASHING) if (ai->flags & FLAG_FLASHING)
return -EIO; return -EIO;
iobuf = kmalloc(RIDS_SIZE, GFP_KERNEL);
if (!iobuf)
return -ENOMEM;
ridcode = 0; ridcode = 0;
writer = do_writerid; writer = do_writerid;
...@@ -6003,47 +6095,52 @@ static int writerids(struct net_device *dev, aironet_ioctl *comp) { ...@@ -6003,47 +6095,52 @@ static int writerids(struct net_device *dev, aironet_ioctl *comp) {
* same with MAC off * same with MAC off
*/ */
case AIROPMACON: case AIROPMACON:
if (enable_MAC(ai, &rsp) != 0) if (enable_MAC(ai, &rsp, 1) != 0)
ret = -EIO; return -EIO;
goto wr_free; return 0;
/* /*
* Evidently this code in the airo driver does not get a symbol * Evidently this code in the airo driver does not get a symbol
* as disable_MAC. it's probably so short the compiler does not gen one. * as disable_MAC. it's probably so short the compiler does not gen one.
*/ */
case AIROPMACOFF: case AIROPMACOFF:
disable_MAC(ai); disable_MAC(ai, 1);
goto wr_free; return 0;
/* This command merely clears the counts does not actually store any data /* This command merely clears the counts does not actually store any data
* only reads rid. But as it changes the cards state, I put it in the * only reads rid. But as it changes the cards state, I put it in the
* writerid routines. * writerid routines.
*/ */
case AIROPSTCLR: case AIROPSTCLR:
PC4500_readrid(ai,RID_STATSDELTACLEAR,iobuf,RIDS_SIZE); if ((iobuf = kmalloc(RIDS_SIZE, GFP_KERNEL)) == NULL)
return -ENOMEM;
PC4500_readrid(ai,RID_STATSDELTACLEAR,iobuf,RIDS_SIZE, 1);
enabled = ai->micstats.enabled; enabled = ai->micstats.enabled;
memset(&ai->micstats,0,sizeof(ai->micstats)); memset(&ai->micstats,0,sizeof(ai->micstats));
ai->micstats.enabled = enabled; ai->micstats.enabled = enabled;
if (copy_to_user(comp->data, iobuf, if (copy_to_user(comp->data, iobuf,
min((int)comp->len, (int)RIDS_SIZE))) min((int)comp->len, (int)RIDS_SIZE))) {
ret = -EFAULT; kfree (iobuf);
goto wr_free; return -EFAULT;
}
kfree (iobuf);
return 0;
default: default:
ret = -EOPNOTSUPP; /* Blarg! */ return -EOPNOTSUPP; /* Blarg! */
goto wr_free;
} }
if(comp->len > RIDS_SIZE)
return -EINVAL;
if (comp->len > RIDS_SIZE) { if ((iobuf = kmalloc(RIDS_SIZE, GFP_KERNEL)) == NULL)
ret = -EINVAL; return -ENOMEM;
goto wr_free;
}
if (copy_from_user(iobuf,comp->data,comp->len)) { if (copy_from_user(iobuf,comp->data,comp->len)) {
ret = -EFAULT; kfree (iobuf);
goto wr_free; return -EFAULT;
} }
if (comp->command == AIROPCFG) { if (comp->command == AIROPCFG) {
...@@ -6058,11 +6155,12 @@ static int writerids(struct net_device *dev, aironet_ioctl *comp) { ...@@ -6058,11 +6155,12 @@ static int writerids(struct net_device *dev, aironet_ioctl *comp) {
ai->flags &= ~FLAG_ADHOC; ai->flags &= ~FLAG_ADHOC;
} }
if((*writer)(ai, ridcode, iobuf,comp->len)) if((*writer)(ai, ridcode, iobuf,comp->len,1)) {
ret = -EIO; kfree (iobuf);
wr_free: return -EIO;
kfree(iobuf); }
return ret; kfree (iobuf);
return 0;
} }
/***************************************************************************** /*****************************************************************************
...@@ -6140,7 +6238,7 @@ int flashcard(struct net_device *dev, aironet_ioctl *comp) { ...@@ -6140,7 +6238,7 @@ int flashcard(struct net_device *dev, aironet_ioctl *comp) {
*/ */
int cmdreset(struct airo_info *ai) { int cmdreset(struct airo_info *ai) {
disable_MAC(ai); disable_MAC(ai, 1);
if(!waitbusy (ai)){ if(!waitbusy (ai)){
printk(KERN_INFO "Waitbusy hang before RESET\n"); printk(KERN_INFO "Waitbusy hang before RESET\n");
......
#ifndef __WL3501_H__
#define __WL3501_H__
#include <linux/spinlock.h>
#include "ieee802_11.h"
/* define for WLA 2.0 */
#define WL3501_BLKSZ 256
/*
* ID for input Signals of DRIVER block
* bit[7-5] is block ID: 000
* bit[4-0] is signal ID
*/
enum wl3501_signals {
WL3501_SIG_ALARM,
WL3501_SIG_MD_CONFIRM,
WL3501_SIG_MD_IND,
WL3501_SIG_ASSOC_CONFIRM,
WL3501_SIG_ASSOC_IND,
WL3501_SIG_AUTH_CONFIRM,
WL3501_SIG_AUTH_IND,
WL3501_SIG_DEAUTH_CONFIRM,
WL3501_SIG_DEAUTH_IND,
WL3501_SIG_DISASSOC_CONFIRM,
WL3501_SIG_DISASSOC_IND,
WL3501_SIG_GET_CONFIRM,
WL3501_SIG_JOIN_CONFIRM,
WL3501_SIG_PWR_MGMT_CONFIRM,
WL3501_SIG_REASSOC_CONFIRM,
WL3501_SIG_REASSOC_IND,
WL3501_SIG_SCAN_CONFIRM,
WL3501_SIG_SET_CONFIRM,
WL3501_SIG_START_CONFIRM,
WL3501_SIG_RESYNC_CONFIRM,
WL3501_SIG_SITE_CONFIRM,
WL3501_SIG_SAVE_CONFIRM,
WL3501_SIG_RFTEST_CONFIRM,
/*
* ID for input Signals of MLME block
* bit[7-5] is block ID: 010
* bit[4-0] is signal ID
*/
WL3501_SIG_ASSOC_REQ = 0x20,
WL3501_SIG_AUTH_REQ,
WL3501_SIG_DEAUTH_REQ,
WL3501_SIG_DISASSOC_REQ,
WL3501_SIG_GET_REQ,
WL3501_SIG_JOIN_REQ,
WL3501_SIG_PWR_MGMT_REQ,
WL3501_SIG_REASSOC_REQ,
WL3501_SIG_SCAN_REQ,
WL3501_SIG_SET_REQ,
WL3501_SIG_START_REQ,
WL3501_SIG_MD_REQ,
WL3501_SIG_RESYNC_REQ,
WL3501_SIG_SITE_REQ,
WL3501_SIG_SAVE_REQ,
WL3501_SIG_RF_TEST_REQ,
WL3501_SIG_MM_CONFIRM = 0x60,
WL3501_SIG_MM_IND,
};
enum wl3501_mib_attribs {
WL3501_MIB_ATTR_STATION_ID,
WL3501_MIB_ATTR_AUTH_ALGORITHMS,
WL3501_MIB_ATTR_AUTH_TYPE,
WL3501_MIB_ATTR_MEDIUM_OCCUPANCY_LIMIT,
WL3501_MIB_ATTR_CF_POLLABLE,
WL3501_MIB_ATTR_CFP_PERIOD,
WL3501_MIB_ATTR_CFPMAX_DURATION,
WL3501_MIB_ATTR_AUTH_RESP_TMOUT,
WL3501_MIB_ATTR_RX_DTIMS,
WL3501_MIB_ATTR_PRIV_OPT_IMPLEMENTED,
WL3501_MIB_ATTR_PRIV_INVOKED,
WL3501_MIB_ATTR_WEP_DEFAULT_KEYS,
WL3501_MIB_ATTR_WEP_DEFAULT_KEY_ID,
WL3501_MIB_ATTR_WEP_KEY_MAPPINGS,
WL3501_MIB_ATTR_WEP_KEY_MAPPINGS_LEN,
WL3501_MIB_ATTR_EXCLUDE_UNENCRYPTED,
WL3501_MIB_ATTR_WEP_ICV_ERROR_COUNT,
WL3501_MIB_ATTR_WEP_UNDECRYPTABLE_COUNT,
WL3501_MIB_ATTR_WEP_EXCLUDED_COUNT,
WL3501_MIB_ATTR_MAC_ADDR,
WL3501_MIB_ATTR_GROUP_ADDRS,
WL3501_MIB_ATTR_RTS_THRESHOLD,
WL3501_MIB_ATTR_SHORT_RETRY_LIMIT,
WL3501_MIB_ATTR_LONG_RETRY_LIMIT,
WL3501_MIB_ATTR_FRAG_THRESHOLD,
WL3501_MIB_ATTR_MAX_TX_MSDU_LIFETIME,
WL3501_MIB_ATTR_MAX_RX_LIFETIME,
WL3501_MIB_ATTR_MANUFACTURER_ID,
WL3501_MIB_ATTR_PRODUCT_ID,
WL3501_MIB_ATTR_TX_FRAG_COUNT,
WL3501_MIB_ATTR_MULTICAST_TX_FRAME_COUNT,
WL3501_MIB_ATTR_FAILED_COUNT,
WL3501_MIB_ATTR_RX_FRAG_COUNT,
WL3501_MIB_ATTR_MULTICAST_RX_COUNT,
WL3501_MIB_ATTR_FCS_ERROR_COUNT,
WL3501_MIB_ATTR_RETRY_COUNT,
WL3501_MIB_ATTR_MULTIPLE_RETRY_COUNT,
WL3501_MIB_ATTR_RTS_SUCCESS_COUNT,
WL3501_MIB_ATTR_RTS_FAILURE_COUNT,
WL3501_MIB_ATTR_ACK_FAILURE_COUNT,
WL3501_MIB_ATTR_FRAME_DUPLICATE_COUNT,
WL3501_MIB_ATTR_PHY_TYPE,
WL3501_MIB_ATTR_REG_DOMAINS_SUPPORT,
WL3501_MIB_ATTR_CURRENT_REG_DOMAIN,
WL3501_MIB_ATTR_SLOT_TIME,
WL3501_MIB_ATTR_CCA_TIME,
WL3501_MIB_ATTR_RX_TX_TURNAROUND_TIME,
WL3501_MIB_ATTR_TX_PLCP_DELAY,
WL3501_MIB_ATTR_RX_TX_SWITCH_TIME,
WL3501_MIB_ATTR_TX_RAMP_ON_TIME,
WL3501_MIB_ATTR_TX_RF_DELAY,
WL3501_MIB_ATTR_SIFS_TIME,
WL3501_MIB_ATTR_RX_RF_DELAY,
WL3501_MIB_ATTR_RX_PLCP_DELAY,
WL3501_MIB_ATTR_MAC_PROCESSING_DELAY,
WL3501_MIB_ATTR_TX_RAMP_OFF_TIME,
WL3501_MIB_ATTR_PREAMBLE_LEN,
WL3501_MIB_ATTR_PLCP_HEADER_LEN,
WL3501_MIB_ATTR_MPDU_DURATION_FACTOR,
WL3501_MIB_ATTR_AIR_PROPAGATION_TIME,
WL3501_MIB_ATTR_TEMP_TYPE,
WL3501_MIB_ATTR_CW_MIN,
WL3501_MIB_ATTR_CW_MAX,
WL3501_MIB_ATTR_SUPPORT_DATA_RATES_TX,
WL3501_MIB_ATTR_SUPPORT_DATA_RATES_RX,
WL3501_MIB_ATTR_MPDU_MAX_LEN,
WL3501_MIB_ATTR_SUPPORT_TX_ANTENNAS,
WL3501_MIB_ATTR_CURRENT_TX_ANTENNA,
WL3501_MIB_ATTR_SUPPORT_RX_ANTENNAS,
WL3501_MIB_ATTR_DIVERSITY_SUPPORT,
WL3501_MIB_ATTR_DIVERSITY_SELECTION_RS,
WL3501_MIB_ATTR_NR_SUPPORTED_PWR_LEVELS,
WL3501_MIB_ATTR_TX_PWR_LEVEL1,
WL3501_MIB_ATTR_TX_PWR_LEVEL2,
WL3501_MIB_ATTR_TX_PWR_LEVEL3,
WL3501_MIB_ATTR_TX_PWR_LEVEL4,
WL3501_MIB_ATTR_TX_PWR_LEVEL5,
WL3501_MIB_ATTR_TX_PWR_LEVEL6,
WL3501_MIB_ATTR_TX_PWR_LEVEL7,
WL3501_MIB_ATTR_TX_PWR_LEVEL8,
WL3501_MIB_ATTR_CURRENT_TX_PWR_LEVEL,
WL3501_MIB_ATTR_CURRENT_CHAN,
WL3501_MIB_ATTR_CCA_MODE_SUPPORTED,
WL3501_MIB_ATTR_CURRENT_CCA_MODE,
WL3501_MIB_ATTR_ED_THRESHOLD,
WL3501_MIB_ATTR_SINTHESIZER_LOCKED,
WL3501_MIB_ATTR_CURRENT_PWR_STATE,
WL3501_MIB_ATTR_DOZE_TURNON_TIME,
WL3501_MIB_ATTR_RCR33,
WL3501_MIB_ATTR_DEFAULT_CHAN,
WL3501_MIB_ATTR_SSID,
WL3501_MIB_ATTR_PWR_MGMT_ENABLE,
WL3501_MIB_ATTR_NET_CAPABILITY,
WL3501_MIB_ATTR_ROUTING,
};
enum wl3501_net_type {
WL3501_NET_TYPE_INFRA,
WL3501_NET_TYPE_ADHOC,
WL3501_NET_TYPE_ANY_BSS,
};
enum wl3501_scan_type {
WL3501_SCAN_TYPE_ACTIVE,
WL3501_SCAN_TYPE_PASSIVE,
};
enum wl3501_tx_result {
WL3501_TX_RESULT_SUCCESS,
WL3501_TX_RESULT_NO_BSS,
WL3501_TX_RESULT_RETRY_LIMIT,
};
enum wl3501_sys_type {
WL3501_SYS_TYPE_OPEN,
WL3501_SYS_TYPE_SHARE_KEY,
};
enum wl3501_status {
WL3501_STATUS_SUCCESS,
WL3501_STATUS_INVALID,
WL3501_STATUS_TIMEOUT,
WL3501_STATUS_REFUSED,
WL3501_STATUS_MANY_REQ,
WL3501_STATUS_ALREADY_BSS,
};
#define WL3501_MGMT_CAPABILITY_ESS 0x0001 /* see 802.11 p.58 */
#define WL3501_MGMT_CAPABILITY_IBSS 0x0002 /* - " - */
#define WL3501_MGMT_CAPABILITY_CF_POLLABLE 0x0004 /* - " - */
#define WL3501_MGMT_CAPABILITY_CF_POLL_REQUEST 0x0008 /* - " - */
#define WL3501_MGMT_CAPABILITY_PRIVACY 0x0010 /* - " - */
#define IW_REG_DOMAIN_FCC 0x10 /* Channel 1 to 11 USA */
#define IW_REG_DOMAIN_DOC 0x20 /* Channel 1 to 11 Canada */
#define IW_REG_DOMAIN_ETSI 0x30 /* Channel 1 to 13 Europe */
#define IW_REG_DOMAIN_SPAIN 0x31 /* Channel 10 to 11 Spain */
#define IW_REG_DOMAIN_FRANCE 0x32 /* Channel 10 to 13 France */
#define IW_REG_DOMAIN_MKK 0x40 /* Channel 14 Japan */
#define IW_REG_DOMAIN_MKK1 0x41 /* Channel 1-14 Japan */
#define IW_REG_DOMAIN_ISRAEL 0x50 /* Channel 3 - 9 Israel */
#define WL3501_ESSID_MAX_LEN (IW_ESSID_MAX_SIZE + 2)
struct wl3501_tx_hdr {
u16 tx_cnt;
u8 sync[16];
u16 sfd;
u8 signal;
u8 service;
u16 len;
u16 crc16;
u16 frame_ctrl;
u16 duration_id;
u8 addr1[ETH_ALEN];
u8 addr2[ETH_ALEN];
u8 addr3[ETH_ALEN];
u16 seq_ctrl;
u8 addr4[ETH_ALEN];
};
struct wl3501_rx_hdr {
u16 rx_next_blk;
u16 rc_next_frame_blk;
u8 rx_blk_ctrl;
u8 rx_next_frame;
u8 rx_next_frame1;
u8 rssi;
char time[8];
u8 signal;
u8 service;
u16 len;
u16 crc16;
u16 frame_ctrl;
u16 duration;
u8 addr1[ETH_ALEN];
u8 addr2[ETH_ALEN];
u8 addr3[ETH_ALEN];
u16 seq;
u8 addr4[ETH_ALEN];
};
struct wl3501_start_req {
u16 next_blk;
u8 sig_id;
u8 bss_type;
u16 beacon_period;
u16 dtim_period;
u16 probe_delay;
u16 cap_info;
char ssid[WL3501_ESSID_MAX_LEN];
u8 bss_basic_rate_set[10];
u8 operational_rate_set[10];
u8 cf_pset[8];
u8 phy_pset[3];
u8 ibss_pset[4];
};
struct wl3501_assoc_req {
u16 next_blk;
u8 sig_id;
u8 reserved;
u16 timeout;
u16 cap_info;
u16 listen_interval;
u8 mac_addr[ETH_ALEN];
};
struct wl3501_assoc_confirm {
u16 next_blk;
u8 sig_id;
u8 reserved;
u16 status;
};
struct wl3501_assoc_ind {
u16 next_blk;
u8 sig_id;
u8 mac_addr[ETH_ALEN];
};
struct wl3501_auth_req {
u16 next_blk;
u8 sig_id;
u8 reserved;
u16 type;
u16 timeout;
u8 mac_addr[ETH_ALEN];
};
struct wl3501_auth_confirm {
u16 next_blk;
u8 sig_id;
u8 reserved;
u16 type;
u16 status;
u8 mac_addr[ETH_ALEN];
};
struct wl3501_get_req {
u16 next_blk;
u8 sig_id;
u8 reserved;
u16 mib_attrib;
};
struct wl3501_get_confirm {
u16 next_blk;
u8 sig_id;
u8 reserved;
u16 mib_status;
u16 mib_attrib;
u8 mib_value[100];
};
struct wl3501_join_req {
u16 next_blk;
u8 sig_id;
u8 reserved;
u8 operational_rate_set[10];
u16 reserved2;
u16 timeout;
u16 probe_delay;
u8 timestamp[8];
u8 local_time[8];
u16 beacon_period;
u16 dtim_period;
u16 cap_info;
u8 bss_type;
u8 bssid[ETH_ALEN];
char ssid[WL3501_ESSID_MAX_LEN];
u8 phy_pset[3];
u8 cf_pset[8];
u8 ibss_pset[4];
u8 bss_basic_rate_set[10];
};
struct wl3501_join_confirm {
u16 next_blk;
u8 sig_id;
u8 reserved;
u16 status;
};
struct wl3501_pwr_mgmt_req {
u16 next_blk;
u8 sig_id;
u8 pwr_save;
u8 wake_up;
u8 receive_dtims;
};
struct wl3501_pwr_mgmt_confirm {
u16 next_blk;
u8 sig_id;
u8 reserved;
u16 status;
};
struct wl3501_scan_req {
u16 next_blk;
u8 sig_id;
u8 bss_type;
u16 probe_delay;
u16 min_chan_time;
u16 max_chan_time;
u8 chan_list[14];
u8 bssid[ETH_ALEN];
char ssid[WL3501_ESSID_MAX_LEN];
enum wl3501_scan_type scan_type;
};
struct wl3501_scan_confirm {
u16 next_blk;
u8 sig_id;
u8 reserved;
u16 status;
char timestamp[8];
char localtime[8];
u16 beacon_period;
u16 dtim_period;
u16 cap_info;
u8 bss_type;
u8 bssid[ETH_ALEN];
char ssid[WL3501_ESSID_MAX_LEN];
u8 phy_pset[3];
u8 cf_pset[8];
u8 ibss_pset[4];
u8 bss_basic_rate_set[10];
u8 rssi;
};
struct wl3501_start_confirm {
u16 next_blk;
u8 sig_id;
u8 reserved;
u16 status;
};
struct wl3501_md_req {
u16 next_blk;
u8 sig_id;
u8 routing;
u16 data;
u16 size;
u8 pri;
u8 service_class;
u8 daddr[ETH_ALEN];
u8 saddr[ETH_ALEN];
};
struct wl3501_md_ind {
u16 next_blk;
u8 sig_id;
u8 routing;
u16 data;
u16 size;
u8 reception;
u8 pri;
u8 service_class;
u8 daddr[ETH_ALEN];
u8 saddr[ETH_ALEN];
};
struct wl3501_md_confirm {
u16 next_blk;
u8 sig_id;
u8 reserved;
u16 data;
u8 status;
u8 pri;
u8 service_class;
};
struct wl3501_resync_req {
u16 next_blk;
u8 sig_id;
};
/* Definitions for supporting clone adapters. */
/* System Interface Registers (SIR space) */
#define WL3501_NIC_GCR ((u8)0x00) /* SIR0 - General Conf Register */
#define WL3501_NIC_BSS ((u8)0x01) /* SIR1 - Bank Switching Select Reg */
#define WL3501_NIC_LMAL ((u8)0x02) /* SIR2 - Local Mem addr Reg [7:0] */
#define WL3501_NIC_LMAH ((u8)0x03) /* SIR3 - Local Mem addr Reg [14:8] */
#define WL3501_NIC_IODPA ((u8)0x04) /* SIR4 - I/O Data Port A */
#define WL3501_NIC_IODPB ((u8)0x05) /* SIR5 - I/O Data Port B */
#define WL3501_NIC_IODPC ((u8)0x06) /* SIR6 - I/O Data Port C */
#define WL3501_NIC_IODPD ((u8)0x07) /* SIR7 - I/O Data Port D */
/* Bits in GCR */
#define WL3501_GCR_SWRESET ((u8)0x80)
#define WL3501_GCR_CORESET ((u8)0x40)
#define WL3501_GCR_DISPWDN ((u8)0x20)
#define WL3501_GCR_ECWAIT ((u8)0x10)
#define WL3501_GCR_ECINT ((u8)0x08)
#define WL3501_GCR_INT2EC ((u8)0x04)
#define WL3501_GCR_ENECINT ((u8)0x02)
#define WL3501_GCR_DAM ((u8)0x01)
/* Bits in BSS (Bank Switching Select Register) */
#define WL3501_BSS_FPAGE0 ((u8)0x20) /* Flash memory page0 */
#define WL3501_BSS_FPAGE1 ((u8)0x28)
#define WL3501_BSS_FPAGE2 ((u8)0x30)
#define WL3501_BSS_FPAGE3 ((u8)0x38)
#define WL3501_BSS_SPAGE0 ((u8)0x00) /* SRAM page0 */
#define WL3501_BSS_SPAGE1 ((u8)0x08)
#define WL3501_BSS_SPAGE2 ((u8)0x10)
#define WL3501_BSS_SPAGE3 ((u8)0x18)
/* Define Driver Interface */
/* Refer IEEE 802.11 */
/* Tx packet header, include PLCP and MPDU */
/* Tx PLCP Header */
struct wl3501_80211_tx_plcp_hdr {
u8 sync[16];
u16 sfd;
u8 signal;
u8 service;
u16 len;
u16 crc16;
} __attribute__ ((packed));
struct wl3501_80211_tx_hdr {
struct wl3501_80211_tx_plcp_hdr pclp_hdr;
struct ieee802_11_hdr mac_hdr;
} __attribute__ ((packed));
/*
Reserve the beginning Tx space for descriptor use.
TxBlockOffset --> *----*----*----*----* \
(TxFreeDesc) | 0 | 1 | 2 | 3 | \
| 4 | 5 | 6 | 7 | |
| 8 | 9 | 10 | 11 | TX_DESC * 20
| 12 | 13 | 14 | 15 | |
| 16 | 17 | 18 | 19 | /
TxBufferBegin --> *----*----*----*----* /
(TxBufferHead) | |
(TxBufferTail) | |
| Send Buffer |
| |
| |
*-------------------*
TxBufferEnd -------------------------/
*/
struct wl3501_card {
int base_addr;
u8 mac_addr[ETH_ALEN];
spinlock_t lock;
wait_queue_head_t wait;
struct wl3501_get_confirm sig_get_confirm;
struct wl3501_pwr_mgmt_confirm sig_pwr_mgmt_confirm;
u16 tx_buffer_size;
u16 tx_buffer_head;
u16 tx_buffer_tail;
u16 tx_buffer_cnt;
u16 esbq_req_start;
u16 esbq_req_end;
u16 esbq_req_head;
u16 esbq_req_tail;
u16 esbq_confirm_start;
u16 esbq_confirm_end;
u16 esbq_confirm;
u8 essid[WL3501_ESSID_MAX_LEN];
u8 bssid[ETH_ALEN];
int net_type;
u8 keep_essid[WL3501_ESSID_MAX_LEN];
char nick[32];
char card_name[32];
char firmware_date[32];
u8 chan;
u8 cap_info;
u16 start_seg;
u16 bss_cnt;
u16 join_sta_bss;
u8 rssi;
u8 adhoc_times;
u8 reg_domain;
u8 version[2];
struct wl3501_scan_confirm bss_set[20];
struct net_device_stats stats;
struct iw_statistics wstats;
struct iw_spy_data spy_data;
struct dev_node_t node;
};
#endif
/*
* WL3501 Wireless LAN PCMCIA Card Driver for Linux
* Written originally for Linux 2.0.30 by Fox Chen, mhchen@golf.ccl.itri.org.tw
* Ported to 2.2, 2.4 & 2.5 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
* Wireless extensions in 2.4 by Gustavo Niemeyer <niemeyer@conectiva.com>
*
* References used by Fox Chen while writing the original driver for 2.0.30:
*
* 1. WL24xx packet drivers (tooasm.asm)
* 2. Access Point Firmware Interface Specification for IEEE 802.11 SUTRO
* 3. IEEE 802.11
* 4. Linux network driver (/usr/src/linux/drivers/net)
* 5. ISA card driver - wl24.c
* 6. Linux PCMCIA skeleton driver - skeleton.c
* 7. Linux PCMCIA 3c589 network driver - 3c589_cs.c
*
* Tested with WL2400 firmware 1.2, Linux 2.0.30, and pcmcia-cs-2.9.12
* 1. Performance: about 165 Kbytes/sec in TCP/IP with Ad-Hoc mode.
* rsh 192.168.1.3 "dd if=/dev/zero bs=1k count=1000" > /dev/null
* (Specification 2M bits/sec. is about 250 Kbytes/sec., but we must deduct
* ETHER/IP/UDP/TCP header, and acknowledgement overhead)
*
* Tested with Planet AP in 2.4.17, 184 Kbytes/s in UDP in Infrastructure mode,
* 173 Kbytes/s in TCP.
*
* Tested with Planet AP in 2.5.73-bk, 216 Kbytes/s in Infrastructure mode
* with a SMP machine (dual pentium 100), using pktgen, 432 pps (pkt_size = 60)
*/
#undef REALLY_SLOW_IO /* most systems can safely undef this */
#include <linux/config.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/ethtool.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fcntl.h>
#include <linux/if_arp.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/wireless.h>
#include <net/iw_handler.h>
#include <pcmcia/version.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ds.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include "wl3501.h"
#ifndef __i386__
#define slow_down_io()
#endif
/* For rough constant delay */
#define WL3501_NOPLOOP(n) { int x = 0; while (x++ < n) slow_down_io(); }
/*
* All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If you do not
* define PCMCIA_DEBUG at all, all the debug code will be left out. If you
* compile with PCMCIA_DEBUG=0, the debug code will be present but disabled --
* but it can then be enabled for specific modules at load time with a
* 'pc_debug=#' option to insmod.
*/
#define PCMCIA_DEBUG 0
#ifdef PCMCIA_DEBUG
static int pc_debug = PCMCIA_DEBUG;
MODULE_PARM(pc_debug, "i");
#define dprintk(n, format, args...) \
{ if (pc_debug > (n)) \
printk(KERN_INFO "%s: " format "\n", __FUNCTION__, ##args); }
#else
#define dprintk(n, format, args...)
#endif
#define wl3501_outb(a, b) { outb(a, b); slow_down_io(); }
#define wl3501_outb_p(a, b) { outb_p(a, b); slow_down_io(); }
#define wl3501_outsb(a, b, c) { outsb(a, b, c); slow_down_io(); }
#define WL3501_RELEASE_TIMEOUT (25 * HZ)
#define WL3501_MAX_ADHOC_TRIES 16
#define WL3501_RESUME 0
#define WL3501_SUSPEND 1
/* Parameters that can be set with 'insmod' */
/* Bit map of interrupts to choose from */
/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */
static unsigned long wl3501_irq_mask = 0xdeb8;
static int wl3501_irq_list[4] = { -1 };
/*
* The event() function is this driver's Card Services event handler. It will
* be called by Card Services when an appropriate card status event is
* received. The config() and release() entry points are used to configure or
* release a socket, in response to card insertion and ejection events. They
* are invoked from the wl24 event handler.
*/
static void wl3501_config(dev_link_t *link);
static void wl3501_release(unsigned long arg);
static int wl3501_event(event_t event, int pri, event_callback_args_t *args);
/*
* The dev_info variable is the "key" that is used to match up this
* device driver with appropriate cards, through the card configuration
* database.
*/
static dev_info_t wl3501_dev_info = "wl3501_cs";
static int wl3501_chan2freq[] = {
[0] = 2412, [1] = 2417, [2] = 2422, [3] = 2427, [4] = 2432,
[5] = 2437, [6] = 2442, [7] = 2447, [8] = 2452, [9] = 2457,
[10] = 2462, [11] = 2467, [12] = 2472, [13] = 2477,
};
static const struct {
int reg_domain;
int min, max, deflt;
} iw_channel_table[] = {
{
.reg_domain = IW_REG_DOMAIN_FCC,
.min = 1,
.max = 11,
.deflt = 1,
},
{
.reg_domain = IW_REG_DOMAIN_DOC,
.min = 1,
.max = 11,
.deflt = 1,
},
{
.reg_domain = IW_REG_DOMAIN_ETSI,
.min = 1,
.max = 13,
.deflt = 1,
},
{
.reg_domain = IW_REG_DOMAIN_SPAIN,
.min = 10,
.max = 11,
.deflt = 10,
},
{
.reg_domain = IW_REG_DOMAIN_FRANCE,
.min = 10,
.max = 13,
.deflt = 10,
},
{
.reg_domain = IW_REG_DOMAIN_MKK,
.min = 14,
.max = 14,
.deflt = 14,
},
{
.reg_domain = IW_REG_DOMAIN_MKK1,
.min = 1,
.max = 14,
.deflt = 1,
},
{
.reg_domain = IW_REG_DOMAIN_ISRAEL,
.min = 3,
.max = 9,
.deflt = 9,
},
};
/**
* iw_valid_channel - validate channel in regulatory domain
* @reg_comain - regulatory domain
* @channel - channel to validate
*
* Returns 0 if invalid in the specified regulatory domain, non-zero if valid.
*/
static int iw_valid_channel(int reg_domain, int channel)
{
int i, rc = 0;
for (i = 0; i < ARRAY_SIZE(iw_channel_table); i++)
if (reg_domain == iw_channel_table[i].reg_domain) {
rc = channel >= iw_channel_table[i].min &&
channel <= iw_channel_table[i].max;
break;
}
return rc;
}
/**
* iw_default_channel - get default channel for a regulatory domain
* @reg_comain - regulatory domain
*
* Returns the default channel for a regulatory domain
*/
static int iw_default_channel(int reg_domain)
{
int i, rc = 1;
for (i = 0; i < ARRAY_SIZE(iw_channel_table); i++)
if (reg_domain == iw_channel_table[i].reg_domain) {
rc = iw_channel_table[i].deflt;
break;
}
return rc;
}
/*
* A linked list of "instances" of the wl24 device. Each actual PCMCIA card
* corresponds to one device instance, and is described by one dev_link_t
* structure (defined in ds.h).
*
* You may not want to use a linked list for this -- for example, the memory
* card driver uses an array of dev_link_t pointers, where minor device numbers
* are used to derive the corresponding array index.
*/
static dev_link_t *wl3501_dev_list;
static inline void wl3501_switch_page(struct wl3501_card *this, u8 page)
{
wl3501_outb(page, this->base_addr + WL3501_NIC_BSS);
}
/*
* Get Ethernet MAC addresss.
*
* WARNING: We switch to FPAGE0 and switc back again.
* Making sure there is no other WL function beening called by ISR.
*/
static int wl3501_get_flash_mac_addr(struct wl3501_card *this)
{
int base_addr = this->base_addr;
/* get MAC addr */
wl3501_outb(WL3501_BSS_FPAGE3, base_addr + WL3501_NIC_BSS); /* BSS */
wl3501_outb(0x00, base_addr + WL3501_NIC_LMAL); /* LMAL */
wl3501_outb(0x40, base_addr + WL3501_NIC_LMAH); /* LMAH */
/* wait for reading EEPROM */
WL3501_NOPLOOP(100);
this->mac_addr[0] = inb(base_addr + WL3501_NIC_IODPA);
WL3501_NOPLOOP(100);
this->mac_addr[1] = inb(base_addr + WL3501_NIC_IODPA);
WL3501_NOPLOOP(100);
this->mac_addr[2] = inb(base_addr + WL3501_NIC_IODPA);
WL3501_NOPLOOP(100);
this->mac_addr[3] = inb(base_addr + WL3501_NIC_IODPA);
WL3501_NOPLOOP(100);
this->mac_addr[4] = inb(base_addr + WL3501_NIC_IODPA);
WL3501_NOPLOOP(100);
this->mac_addr[5] = inb(base_addr + WL3501_NIC_IODPA);
WL3501_NOPLOOP(100);
this->reg_domain = inb(base_addr + WL3501_NIC_IODPA);
WL3501_NOPLOOP(100);
wl3501_outb(WL3501_BSS_FPAGE0, base_addr + WL3501_NIC_BSS);
wl3501_outb(0x04, base_addr + WL3501_NIC_LMAL);
wl3501_outb(0x40, base_addr + WL3501_NIC_LMAH);
WL3501_NOPLOOP(100);
this->version[0] = inb(base_addr + WL3501_NIC_IODPA);
WL3501_NOPLOOP(100);
this->version[1] = inb(base_addr + WL3501_NIC_IODPA);
/* switch to SRAM Page 0 (for safety) */
wl3501_switch_page(this, WL3501_BSS_SPAGE0);
/* The MAC addr should be 00:60:... */
return this->mac_addr[0] == 0x00 && this->mac_addr[1] == 0x60;
}
/**
* wl3501_set_to_wla - Move 'size' bytes from PC to card
* @dest: Card addressing space
* @src: PC addressing space
* @size: Bytes to move
*
* Move 'size' bytes from PC to card. (Shouldn't be interrupted)
*/
void wl3501_set_to_wla(struct wl3501_card *this, u16 dest, void *src, int size)
{
/* switch to SRAM Page 0 */
wl3501_switch_page(this, (dest & 0x8000) ? WL3501_BSS_SPAGE1 :
WL3501_BSS_SPAGE0);
/* set LMAL and LMAH */
wl3501_outb(dest & 0xff, this->base_addr + WL3501_NIC_LMAL);
wl3501_outb(((dest >> 8) & 0x7f), this->base_addr + WL3501_NIC_LMAH);
/* rep out to Port A */
wl3501_outsb(this->base_addr + WL3501_NIC_IODPA, src, size);
}
/**
* wl3501_get_from_wla - Move 'size' bytes from card to PC
* @src: Card addressing space
* @dest: PC addressing space
* @size: Bytes to move
*
* Move 'size' bytes from card to PC. (Shouldn't be interrupted)
*/
void wl3501_get_from_wla(struct wl3501_card *this, u16 src, void *dest,
int size)
{
/* switch to SRAM Page 0 */
wl3501_switch_page(this, (src & 0x8000) ? WL3501_BSS_SPAGE1 :
WL3501_BSS_SPAGE0);
/* set LMAL and LMAH */
wl3501_outb(src & 0xff, this->base_addr + WL3501_NIC_LMAL);
wl3501_outb((src >> 8) & 0x7f, this->base_addr + WL3501_NIC_LMAH);
/* rep get from Port A */
insb(this->base_addr + WL3501_NIC_IODPA, dest, size);
}
/*
* Get/Allocate a free Tx Data Buffer
*
* *--------------*-----------------*----------------------------------*
* | PLCP | MAC Header | DST SRC Data ... |
* | (24 bytes) | (30 bytes) | (6) (6) (Ethernet Row Data) |
* *--------------*-----------------*----------------------------------*
* \ \- IEEE 802.11 -/ \-------------- len --------------/
* \-struct wl3501_80211_tx_hdr--/ \-------- Ethernet Frame -------/
*
* Return = Postion in Card
*/
static u16 wl3501_get_tx_buffer(struct wl3501_card *this, u16 len)
{
u16 next, blk_cnt = 0, zero = 0;
u16 full_len = sizeof(struct wl3501_80211_tx_hdr) + len;
u16 ret = 0;
if (full_len > this->tx_buffer_cnt * 254)
goto out;
ret = this->tx_buffer_head;
while (full_len) {
if (full_len < 254)
full_len = 0;
else
full_len -= 254;
wl3501_get_from_wla(this, this->tx_buffer_head, &next,
sizeof(next));
if (!full_len)
wl3501_set_to_wla(this, this->tx_buffer_head, &zero,
sizeof(zero));
this->tx_buffer_head = next;
blk_cnt++;
/* if buffer is not enough */
if (!next && full_len) {
this->tx_buffer_head = ret;
ret = 0;
goto out;
}
}
this->tx_buffer_cnt -= blk_cnt;
out:
return ret;
}
/*
* Free an allocated Tx Buffer. ptr must be correct position.
*/
static void wl3501_free_tx_buffer(struct wl3501_card *this, u16 ptr)
{
/* check if all space is not free */
if (!this->tx_buffer_head)
this->tx_buffer_head = ptr;
else
wl3501_set_to_wla(this, this->tx_buffer_tail,
&ptr, sizeof(ptr));
while (ptr) {
u16 next;
this->tx_buffer_cnt++;
wl3501_get_from_wla(this, ptr, &next, sizeof(next));
this->tx_buffer_tail = ptr;
ptr = next;
}
}
static int wl3501_esbq_req_test(struct wl3501_card *this)
{
u8 tmp;
wl3501_get_from_wla(this, this->esbq_req_head + 3, &tmp, sizeof(tmp));
return tmp & 0x80;
}
static void wl3501_esbq_req(struct wl3501_card *this, u16 *ptr)
{
u16 tmp = 0;
wl3501_set_to_wla(this, this->esbq_req_head, ptr, 2);
wl3501_set_to_wla(this, this->esbq_req_head + 2, &tmp, sizeof(tmp));
this->esbq_req_head += 4;
if (this->esbq_req_head >= this->esbq_req_end)
this->esbq_req_head = this->esbq_req_start;
}
static int wl3501_esbq_exec(struct wl3501_card *this, void *sig, int sig_size)
{
int rc = -EIO;
if (wl3501_esbq_req_test(this)) {
u16 ptr = wl3501_get_tx_buffer(this, sig_size);
if (ptr) {
wl3501_set_to_wla(this, ptr, sig, sig_size);
wl3501_esbq_req(this, &ptr);
rc = 0;
}
}
return rc;
}
static int wl3501_get_mib_value(struct wl3501_card *this, u8 index,
void *bf, int size)
{
struct wl3501_get_req sig = {
.sig_id = WL3501_SIG_GET_REQ,
.mib_attrib = index,
};
unsigned long flags;
int rc = -EIO;
spin_lock_irqsave(&this->lock, flags);
if (wl3501_esbq_req_test(this)) {
u16 ptr = wl3501_get_tx_buffer(this, sizeof(sig));
if (ptr) {
wl3501_set_to_wla(this, ptr, &sig, sizeof(sig));
wl3501_esbq_req(this, &ptr);
this->sig_get_confirm.mib_status = 255;
spin_unlock_irqrestore(&this->lock, flags);
rc = wait_event_interruptible(this->wait,
this->sig_get_confirm.mib_status != 255);
if (!rc)
memcpy(bf, this->sig_get_confirm.mib_value,
size);
goto out;
}
}
spin_unlock_irqrestore(&this->lock, flags);
out:
return rc;
}
static int wl3501_pwr_mgmt(struct wl3501_card *this, int suspend)
{
struct wl3501_pwr_mgmt_req sig = {
.sig_id = WL3501_SIG_PWR_MGMT_REQ,
.pwr_save = suspend,
.wake_up = !suspend,
.receive_dtims = 10,
};
unsigned long flags;
int rc = -EIO;
spin_lock_irqsave(&this->lock, flags);
if (wl3501_esbq_req_test(this)) {
u16 ptr = wl3501_get_tx_buffer(this, sizeof(sig));
if (ptr) {
wl3501_set_to_wla(this, ptr, &sig, sizeof(sig));
wl3501_esbq_req(this, &ptr);
this->sig_pwr_mgmt_confirm.status = 255;
spin_unlock_irqrestore(&this->lock, flags);
rc = wait_event_interruptible(this->wait,
this->sig_pwr_mgmt_confirm.status != 255);
printk(KERN_INFO "%s: %s status=%d\n", __FUNCTION__,
suspend ? "suspend" : "resume",
this->sig_pwr_mgmt_confirm.status);
goto out;
}
}
spin_unlock_irqrestore(&this->lock, flags);
out:
return rc;
}
/**
* wl3501_send_pkt - Send a packet.
* @this - card
*
* Send a packet.
*
* data = Ethernet raw frame. (e.g. data[0] - data[5] is Dest MAC Addr,
* data[6] - data[11] is Src MAC Addr)
* Ref: IEEE 802.11
*/
static int wl3501_send_pkt(struct wl3501_card *this, u8 *data, u16 len)
{
u16 bf, sig_bf, next, tmplen, pktlen;
struct wl3501_md_req sig = {
.sig_id = WL3501_SIG_MD_REQ,
};
u8 *pdata = (char *)data;
int rc = -EIO;
if (wl3501_esbq_req_test(this)) {
sig_bf = wl3501_get_tx_buffer(this, sizeof(sig));
rc = -ENOMEM;
if (!sig_bf) /* No free buffer available */
goto out;
bf = wl3501_get_tx_buffer(this, len + 26 + 24);
if (!bf) {
/* No free buffer available */
wl3501_free_tx_buffer(this, sig_bf);
goto out;
}
rc = 0;
memcpy(&sig.daddr[0], pdata, 12);
pktlen = len - 12;
pdata += 12;
sig.data = bf;
if (((*pdata) * 256 + (*(pdata + 1))) > 1500) {
u8 addr4[ETH_ALEN] = {
[0] = 0xAA, [1] = 0xAA, [2] = 0x03, [4] = 0x00,
};
wl3501_set_to_wla(this, bf + 2 +
offsetof(struct wl3501_tx_hdr, addr4),
addr4, sizeof(addr4));
sig.size = pktlen + 24 + 4 + 6;
if (pktlen > (254 - sizeof(struct wl3501_tx_hdr))) {
tmplen = 254 - sizeof(struct wl3501_tx_hdr);
pktlen -= tmplen;
} else {
tmplen = pktlen;
pktlen = 0;
}
wl3501_set_to_wla(this,
bf + 2 + sizeof(struct wl3501_tx_hdr),
pdata, tmplen);
pdata += tmplen;
wl3501_get_from_wla(this, bf, &next, sizeof(next));
bf = next;
} else {
sig.size = pktlen + 24 + 4 - 2;
pdata += 2;
pktlen -= 2;
if (pktlen > (254 - sizeof(struct wl3501_tx_hdr) + 6)) {
tmplen = 254 - sizeof(struct wl3501_tx_hdr) + 6;
pktlen -= tmplen;
} else {
tmplen = pktlen;
pktlen = 0;
}
wl3501_set_to_wla(this, bf + 2 +
offsetof(struct wl3501_tx_hdr, addr4),
pdata, tmplen);
pdata += tmplen;
wl3501_get_from_wla(this, bf, &next, sizeof(next));
bf = next;
}
while (pktlen > 0) {
if (pktlen > 254) {
tmplen = 254;
pktlen -= 254;
} else {
tmplen = pktlen;
pktlen = 0;
}
wl3501_set_to_wla(this, bf + 2, pdata, tmplen);
pdata += tmplen;
wl3501_get_from_wla(this, bf, &next, sizeof(next));
bf = next;
}
wl3501_set_to_wla(this, sig_bf, &sig, sizeof(sig));
wl3501_esbq_req(this, &sig_bf);
}
out:
return rc;
}
static int wl3501_mgmt_resync(struct wl3501_card *this)
{
struct wl3501_resync_req sig = {
.sig_id = WL3501_SIG_RESYNC_REQ,
};
return wl3501_esbq_exec(this, &sig, sizeof(sig));
}
static inline int wl3501_fw_bss_type(struct wl3501_card *this)
{
return this->net_type == IW_MODE_INFRA ? WL3501_NET_TYPE_INFRA :
WL3501_NET_TYPE_ADHOC;
}
static inline int wl3501_fw_cap_info(struct wl3501_card *this)
{
return this->net_type == IW_MODE_INFRA ? WL3501_MGMT_CAPABILITY_ESS :
WL3501_MGMT_CAPABILITY_IBSS;
}
static int wl3501_mgmt_scan(struct wl3501_card *this, u16 chan_time)
{
struct wl3501_scan_req sig = {
.sig_id = WL3501_SIG_SCAN_REQ,
.scan_type = WL3501_SCAN_TYPE_ACTIVE,
.probe_delay = 0x10,
.min_chan_time = chan_time,
.max_chan_time = chan_time,
.bss_type = wl3501_fw_bss_type(this),
};
this->bss_cnt = this->join_sta_bss = 0;
return wl3501_esbq_exec(this, &sig, sizeof(sig));
}
static int wl3501_mgmt_join(struct wl3501_card *this, u16 stas)
{
struct wl3501_join_req sig = {
.sig_id = WL3501_SIG_JOIN_REQ,
.timeout = 10,
.phy_pset = {
[2] = this->chan,
},
};
memcpy(&sig.beacon_period, &this->bss_set[stas].beacon_period, 72);
return wl3501_esbq_exec(this, &sig, sizeof(sig));
}
static int wl3501_mgmt_start(struct wl3501_card *this)
{
struct wl3501_start_req sig = {
.sig_id = WL3501_SIG_START_REQ,
.beacon_period = 400,
.dtim_period = 1,
.phy_pset = {
[0] = 3, [1] = 1, [2] = this->chan,
},
.bss_basic_rate_set = {
[0] = 0x01, [1] = 0x02, [2] = 0x82, [3] = 0x84,
},
.operational_rate_set = {
[0] = 0x01, [1] = 0x02, [2] = 0x82, [3] = 0x84,
},
.ibss_pset = {
[0] = 6, [1] = 2, [2] = 10,
},
.bss_type = wl3501_fw_bss_type(this),
.cap_info = wl3501_fw_cap_info(this),
};
memcpy(sig.ssid, this->essid, WL3501_ESSID_MAX_LEN);
memcpy(this->keep_essid, this->essid, WL3501_ESSID_MAX_LEN);
return wl3501_esbq_exec(this, &sig, sizeof(sig));
}
static void wl3501_mgmt_scan_confirm(struct wl3501_card *this, u16 addr)
{
u16 i = 0;
int matchflag = 0;
struct wl3501_scan_confirm sig;
dprintk(3, "entry");
wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
if (sig.status == WL3501_STATUS_SUCCESS) {
dprintk(3, "success");
if ((this->net_type == IW_MODE_INFRA &&
(sig.cap_info & WL3501_MGMT_CAPABILITY_ESS)) ||
(this->net_type == IW_MODE_ADHOC &&
(sig.cap_info & WL3501_MGMT_CAPABILITY_IBSS)) ||
this->net_type == IW_MODE_AUTO) {
if (!this->essid[1])
matchflag = 1;
else if (this->essid[1] == 3 &&
!strncmp((char *)&this->essid[2], "ANY", 3))
matchflag = 1;
else if (this->essid[1] != sig.ssid[1])
matchflag = 0;
else if (memcmp(&this->essid[2], &sig.ssid[2],
this->essid[1]))
matchflag = 0;
else
matchflag = 1;
if (matchflag) {
for (i = 0; i < this->bss_cnt; i++) {
if (!memcmp(this->bss_set[i].bssid,
sig.bssid, ETH_ALEN)) {
matchflag = 0;
break;
}
}
}
if (matchflag && (i < 20)) {
memcpy(&this->bss_set[i].beacon_period,
&sig.beacon_period, 73);
this->bss_cnt++;
this->rssi = sig.rssi;
}
}
} else if (sig.status == WL3501_STATUS_TIMEOUT) {
dprintk(3, "timeout");
this->join_sta_bss = 0;
for (i = this->join_sta_bss; i < this->bss_cnt; i++)
if (!wl3501_mgmt_join(this, i))
break;
this->join_sta_bss = i;
if (this->join_sta_bss == this->bss_cnt) {
if (this->net_type == IW_MODE_INFRA)
wl3501_mgmt_scan(this, 100);
else {
this->adhoc_times++;
if (this->adhoc_times > WL3501_MAX_ADHOC_TRIES)
wl3501_mgmt_start(this);
else
wl3501_mgmt_scan(this, 100);
}
}
}
}
/**
* wl3501_block_interrupt - Mask interrupt from SUTRO
* @this - card
*
* Mask interrupt from SUTRO. (i.e. SUTRO cannot interrupt the HOST)
* Return: 1 if interrupt is originally enabled
*/
static int wl3501_block_interrupt(struct wl3501_card *this)
{
u8 old = inb(this->base_addr + WL3501_NIC_GCR);
u8 new = old & (~(WL3501_GCR_ECINT | WL3501_GCR_INT2EC |
WL3501_GCR_ENECINT));
wl3501_outb(new, this->base_addr + WL3501_NIC_GCR);
return old & WL3501_GCR_ENECINT;
}
/**
* wl3501_unblock_interrupt - Enable interrupt from SUTRO
* @this - card
*
* Enable interrupt from SUTRO. (i.e. SUTRO can interrupt the HOST)
* Return: 1 if interrupt is originally enabled
*/
static int wl3501_unblock_interrupt(struct wl3501_card *this)
{
u8 old = inb(this->base_addr + WL3501_NIC_GCR);
u8 new = (old & ~(WL3501_GCR_ECINT | WL3501_GCR_INT2EC)) |
WL3501_GCR_ENECINT;
wl3501_outb(new, this->base_addr + WL3501_NIC_GCR);
return old & WL3501_GCR_ENECINT;
}
/**
* wl3501_receive - Receive data from Receive Queue.
*
* Receive data from Receive Queue.
*
* @this: card
* @bf: address of host
* @size: size of buffer.
*/
static u16 wl3501_receive(struct wl3501_card *this, u8 *bf, u16 size)
{
u16 next_addr, next_addr1;
u8 *data = bf + 12;
size -= 12;
wl3501_get_from_wla(this, this->start_seg + 2,
&next_addr, sizeof(next_addr));
if (size > WL3501_BLKSZ - sizeof(struct wl3501_rx_hdr)) {
wl3501_get_from_wla(this,
this->start_seg +
sizeof(struct wl3501_rx_hdr), data,
WL3501_BLKSZ -
sizeof(struct wl3501_rx_hdr));
size -= WL3501_BLKSZ - sizeof(struct wl3501_rx_hdr);
data += WL3501_BLKSZ - sizeof(struct wl3501_rx_hdr);
} else {
wl3501_get_from_wla(this,
this->start_seg +
sizeof(struct wl3501_rx_hdr),
data, size);
size = 0;
}
while (size > 0) {
if (size > WL3501_BLKSZ - 5) {
wl3501_get_from_wla(this, next_addr + 5, data,
WL3501_BLKSZ - 5);
size -= WL3501_BLKSZ - 5;
data += WL3501_BLKSZ - 5;
wl3501_get_from_wla(this, next_addr + 2, &next_addr1,
sizeof(next_addr1));
next_addr = next_addr1;
} else {
wl3501_get_from_wla(this, next_addr + 5, data, size);
size = 0;
}
}
return 0;
}
static void wl3501_esbq_req_free(struct wl3501_card *this)
{
u8 tmp;
u16 addr;
if (this->esbq_req_head == this->esbq_req_tail)
goto out;
wl3501_get_from_wla(this, this->esbq_req_tail + 3, &tmp, sizeof(tmp));
if (!(tmp & 0x80))
goto out;
wl3501_get_from_wla(this, this->esbq_req_tail, &addr, sizeof(addr));
wl3501_free_tx_buffer(this, addr);
this->esbq_req_tail += 4;
if (this->esbq_req_tail >= this->esbq_req_end)
this->esbq_req_tail = this->esbq_req_start;
out:
return;
}
static int wl3501_esbq_confirm(struct wl3501_card *this)
{
u8 tmp;
wl3501_get_from_wla(this, this->esbq_confirm + 3, &tmp, sizeof(tmp));
return tmp & 0x80;
}
static void wl3501_online(struct net_device *dev)
{
struct wl3501_card *this = dev->priv;
printk(KERN_INFO "%s: Wireless LAN online. BSSID: "
"%02X %02X %02X %02X %02X %02X\n", dev->name,
this->bssid[0], this->bssid[1], this->bssid[2],
this->bssid[3], this->bssid[4], this->bssid[5]);
netif_wake_queue(dev);
}
static void wl3501_esbq_confirm_done(struct wl3501_card *this)
{
u8 tmp = 0;
wl3501_set_to_wla(this, this->esbq_confirm + 3, &tmp, sizeof(tmp));
this->esbq_confirm += 4;
if (this->esbq_confirm >= this->esbq_confirm_end)
this->esbq_confirm = this->esbq_confirm_start;
}
static int wl3501_mgmt_auth(struct wl3501_card *this)
{
struct wl3501_auth_req sig = {
.sig_id = WL3501_SIG_AUTH_REQ,
.type = WL3501_SYS_TYPE_OPEN,
.timeout = 1000,
};
dprintk(3, "entry");
memcpy(sig.mac_addr, this->bssid, ETH_ALEN);
return wl3501_esbq_exec(this, &sig, sizeof(sig));
}
static int wl3501_mgmt_association(struct wl3501_card *this)
{
struct wl3501_assoc_req sig = {
.sig_id = WL3501_SIG_ASSOC_REQ,
.timeout = 1000,
.listen_interval = 5,
.cap_info = this->cap_info,
};
dprintk(3, "entry");
memcpy(sig.mac_addr, this->bssid, ETH_ALEN);
return wl3501_esbq_exec(this, &sig, sizeof(sig));
}
static void wl3501_mgmt_join_confirm(struct net_device *dev, u16 addr)
{
struct wl3501_card *this = dev->priv;
struct wl3501_join_confirm sig;
dprintk(3, "entry");
wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
if (sig.status == WL3501_STATUS_SUCCESS) {
if (this->net_type == IW_MODE_INFRA) {
if (this->join_sta_bss < this->bss_cnt) {
const int i = this->join_sta_bss;
memcpy(this->bssid,
this->bss_set[i].bssid, ETH_ALEN);
this->chan = this->bss_set[i].phy_pset[2];
memcpy(this->keep_essid, this->bss_set[i].ssid,
WL3501_ESSID_MAX_LEN);
wl3501_mgmt_auth(this);
}
} else {
const int i = this->join_sta_bss;
memcpy(this->bssid, this->bss_set[i].bssid, ETH_ALEN);
this->chan = this->bss_set[i].phy_pset[2];
memcpy(this->keep_essid,
this->bss_set[i].ssid, WL3501_ESSID_MAX_LEN);
wl3501_online(dev);
}
} else {
int i;
this->join_sta_bss++;
for (i = this->join_sta_bss; i < this->bss_cnt; i++)
if (!wl3501_mgmt_join(this, i))
break;
this->join_sta_bss = i;
if (this->join_sta_bss == this->bss_cnt) {
if (this->net_type == IW_MODE_INFRA)
wl3501_mgmt_scan(this, 100);
else {
this->adhoc_times++;
if (this->adhoc_times > WL3501_MAX_ADHOC_TRIES)
wl3501_mgmt_start(this);
else
wl3501_mgmt_scan(this, 100);
}
}
}
}
static inline void wl3501_alarm_interrupt(struct net_device *dev,
struct wl3501_card *this)
{
if (this->net_type == IW_MODE_INFRA) {
printk(KERN_INFO "Wireless LAN offline\n");
netif_stop_queue(dev);
wl3501_mgmt_resync(this);
}
}
static inline void wl3501_md_confirm_interrupt(struct net_device *dev,
struct wl3501_card *this,
u16 addr)
{
struct wl3501_md_confirm sig;
dprintk(3, "entry");
wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
wl3501_free_tx_buffer(this, sig.data);
if (netif_queue_stopped(dev))
netif_wake_queue(dev);
}
static inline void wl3501_md_ind_interrupt(struct net_device *dev,
struct wl3501_card *this, u16 addr)
{
struct wl3501_md_ind sig;
struct sk_buff *skb;
u8 rssi, addr4[ETH_ALEN];
u16 pkt_len;
wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
this->start_seg = sig.data;
wl3501_get_from_wla(this,
sig.data + offsetof(struct wl3501_rx_hdr, rssi),
&rssi, sizeof(rssi));
this->rssi = rssi <= 63 ? (rssi * 100) / 64 : 255;
wl3501_get_from_wla(this,
sig.data +
offsetof(struct wl3501_rx_hdr, addr4),
&addr4, sizeof(addr4));
if (!(addr4[0] == 0xAA && addr4[1] == 0xAA &&
addr4[2] == 0x03 && addr4[4] == 0x00)) {
printk(KERN_INFO "Insupported packet type!\n");
return;
}
pkt_len = sig.size + 12 - 24 - 4 - 6;
skb = dev_alloc_skb(pkt_len + 5);
if (!skb) {
printk(KERN_WARNING "%s: Can't alloc a sk_buff of size %d.\n",
dev->name, pkt_len);
this->stats.rx_dropped++;
} else {
skb->dev = dev;
skb_reserve(skb, 2); /* IP headers on 16 bytes boundaries */
eth_copy_and_sum(skb, (unsigned char *)&sig.daddr, 12, 0);
wl3501_receive(this, skb->data, pkt_len);
skb_put(skb, pkt_len);
skb->protocol = eth_type_trans(skb, dev);
dev->last_rx = jiffies;
this->stats.rx_packets++;
this->stats.rx_bytes += skb->len;
netif_rx(skb);
}
}
static inline void wl3501_get_confirm_interrupt(struct wl3501_card *this,
u16 addr, void *sig, int size)
{
dprintk(3, "entry");
wl3501_get_from_wla(this, addr, &this->sig_get_confirm,
sizeof(this->sig_get_confirm));
wake_up(&this->wait);
}
static inline void wl3501_start_confirm_interrupt(struct net_device *dev,
struct wl3501_card *this,
u16 addr)
{
struct wl3501_start_confirm sig;
dprintk(3, "entry");
wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
if (sig.status == WL3501_STATUS_SUCCESS)
netif_wake_queue(dev);
}
static inline void wl3501_assoc_confirm_interrupt(struct net_device *dev,
u16 addr)
{
struct wl3501_card *this = dev->priv;
struct wl3501_assoc_confirm sig;
dprintk(3, "entry");
wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
if (sig.status == WL3501_STATUS_SUCCESS)
wl3501_online(dev);
}
static inline void wl3501_auth_confirm_interrupt(struct wl3501_card *this,
u16 addr)
{
struct wl3501_auth_confirm sig;
dprintk(3, "entry");
wl3501_get_from_wla(this, addr, &sig, sizeof(sig));
if (sig.status == WL3501_STATUS_SUCCESS)
wl3501_mgmt_association(this);
else
wl3501_mgmt_resync(this);
}
static inline void wl3501_rx_interrupt(struct net_device *dev)
{
int morepkts;
u16 addr;
u8 sig_id;
struct wl3501_card *this = dev->priv;
dprintk(3, "entry");
loop:
morepkts = 0;
if (!wl3501_esbq_confirm(this))
goto free;
wl3501_get_from_wla(this, this->esbq_confirm, &addr, sizeof(addr));
wl3501_get_from_wla(this, addr + 2, &sig_id, sizeof(sig_id));
switch (sig_id) {
case WL3501_SIG_DEAUTH_IND:
case WL3501_SIG_DISASSOC_IND:
case WL3501_SIG_ALARM:
wl3501_alarm_interrupt(dev, this);
break;
case WL3501_SIG_MD_CONFIRM:
wl3501_md_confirm_interrupt(dev, this, addr);
break;
case WL3501_SIG_MD_IND:
wl3501_md_ind_interrupt(dev, this, addr);
break;
case WL3501_SIG_GET_CONFIRM:
wl3501_get_confirm_interrupt(this, addr,
&this->sig_get_confirm,
sizeof(this->sig_get_confirm));
break;
case WL3501_SIG_PWR_MGMT_CONFIRM:
wl3501_get_confirm_interrupt(this, addr,
&this->sig_pwr_mgmt_confirm,
sizeof(this->sig_pwr_mgmt_confirm));
break;
case WL3501_SIG_START_CONFIRM:
wl3501_start_confirm_interrupt(dev, this, addr);
break;
case WL3501_SIG_SCAN_CONFIRM:
wl3501_mgmt_scan_confirm(this, addr);
break;
case WL3501_SIG_JOIN_CONFIRM:
wl3501_mgmt_join_confirm(dev, addr);
break;
case WL3501_SIG_ASSOC_CONFIRM:
wl3501_assoc_confirm_interrupt(dev, addr);
break;
case WL3501_SIG_AUTH_CONFIRM:
wl3501_auth_confirm_interrupt(this, addr);
break;
case WL3501_SIG_RESYNC_CONFIRM:
wl3501_mgmt_resync(this); /* FIXME: should be resync_confirm */
break;
}
wl3501_esbq_confirm_done(this);
morepkts = 1;
/* free request if necessary */
free:
wl3501_esbq_req_free(this);
if (morepkts)
goto loop;
}
static inline void wl3501_ack_interrupt(struct wl3501_card *this)
{
wl3501_outb(WL3501_GCR_ECINT, this->base_addr + WL3501_NIC_GCR);
}
/**
* wl3501_interrupt - Hardware interrupt from card.
* @irq - Interrupt number
* @dev_id - net_device
* @regs - registers
*
* We must acknowledge the interrupt as soon as possible, and block the
* interrupt from the same card immediately to prevent re-entry.
*
* Before accessing the Control_Status_Block, we must lock SUTRO first.
* On the other hand, to prevent SUTRO from malfunctioning, we must
* unlock the SUTRO as soon as possible.
*/
static irqreturn_t wl3501_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct net_device *dev = (struct net_device *)dev_id;
struct wl3501_card *this;
int handled = 1;
if (!dev)
goto unknown;
this = dev->priv;
spin_lock(&this->lock);
wl3501_ack_interrupt(this);
wl3501_block_interrupt(this);
wl3501_rx_interrupt(dev);
wl3501_unblock_interrupt(this);
spin_unlock(&this->lock);
out:
return IRQ_RETVAL(handled);
unknown:
handled = 0;
printk(KERN_ERR "%s: irq %d for unknown device.\n", __FUNCTION__, irq);
goto out;
}
static int wl3501_reset_board(struct wl3501_card *this)
{
u8 tmp = 0;
int i, rc = 0;
/* Coreset */
wl3501_outb_p(WL3501_GCR_CORESET, this->base_addr + WL3501_NIC_GCR);
wl3501_outb_p(0, this->base_addr + WL3501_NIC_GCR);
wl3501_outb_p(WL3501_GCR_CORESET, this->base_addr + WL3501_NIC_GCR);
/* Reset SRAM 0x480 to zero */
wl3501_set_to_wla(this, 0x480, &tmp, sizeof(tmp));
/* Start up */
wl3501_outb_p(0, this->base_addr + WL3501_NIC_GCR);
WL3501_NOPLOOP(1024 * 50);
wl3501_unblock_interrupt(this); /* acme: was commented */
/* Polling Self_Test_Status */
for (i = 0; i < 10000; i++) {
wl3501_get_from_wla(this, 0x480, &tmp, sizeof(tmp));
if (tmp == 'W') {
/* firmware complete all test successfully */
tmp = 'A';
wl3501_set_to_wla(this, 0x480, &tmp, sizeof(tmp));
goto out;
}
WL3501_NOPLOOP(10);
}
printk(KERN_WARNING "%s: failed to reset the board!\n", __FUNCTION__);
rc = -ENODEV;
out:
return rc;
}
static int wl3501_init_firmware(struct wl3501_card *this)
{
u16 ptr, next;
int rc = wl3501_reset_board(this);
if (rc)
goto fail;
this->card_name[0] = '\0';
wl3501_get_from_wla(this, 0x1a00,
this->card_name, sizeof(this->card_name));
this->card_name[sizeof(this->card_name) - 1] = '\0';
this->firmware_date[0] = '\0';
wl3501_get_from_wla(this, 0x1a40,
this->firmware_date, sizeof(this->firmware_date));
this->firmware_date[sizeof(this->firmware_date) - 1] = '\0';
/* Switch to SRAM Page 0 */
wl3501_switch_page(this, WL3501_BSS_SPAGE0);
/* Read parameter from card */
wl3501_get_from_wla(this, 0x482, &this->esbq_req_start, 2);
wl3501_get_from_wla(this, 0x486, &this->esbq_req_end, 2);
wl3501_get_from_wla(this, 0x488, &this->esbq_confirm_start, 2);
wl3501_get_from_wla(this, 0x48c, &this->esbq_confirm_end, 2);
wl3501_get_from_wla(this, 0x48e, &this->tx_buffer_head, 2);
wl3501_get_from_wla(this, 0x492, &this->tx_buffer_size, 2);
this->esbq_req_tail = this->esbq_req_head = this->esbq_req_start;
this->esbq_req_end += this->esbq_req_start;
this->esbq_confirm = this->esbq_confirm_start;
this->esbq_confirm_end += this->esbq_confirm_start;
/* Initial Tx Buffer */
this->tx_buffer_cnt = 1;
ptr = this->tx_buffer_head;
next = ptr + WL3501_BLKSZ;
while ((next - this->tx_buffer_head) < this->tx_buffer_size) {
this->tx_buffer_cnt++;
wl3501_set_to_wla(this, ptr, &next, sizeof(next));
ptr = next;
next = ptr + WL3501_BLKSZ;
}
rc = 0;
next = 0;
wl3501_set_to_wla(this, ptr, &next, sizeof(next));
this->tx_buffer_tail = ptr;
out:
return rc;
fail:
printk(KERN_WARNING "%s: failed!\n", __FUNCTION__);
goto out;
}
static int wl3501_close(struct net_device *dev)
{
struct wl3501_card *this = dev->priv;
int rc = -ENODEV;
unsigned long flags;
dev_link_t *link;
spin_lock_irqsave(&this->lock, flags);
/* Check if the device is in wl3501_dev_list */
for (link = wl3501_dev_list; link; link = link->next)
if (link->priv == dev)
break;
if (!link)
goto out;
link->open--;
/* Stop wl3501_hard_start_xmit() from now on */
netif_stop_queue(dev);
wl3501_ack_interrupt(this);
/* Mask interrupts from the SUTRO */
wl3501_block_interrupt(this);
if (link->state & DEV_STALE_CONFIG) {
link->release.expires = jiffies + WL3501_RELEASE_TIMEOUT;
link->state |= DEV_RELEASE_PENDING;
add_timer(&link->release);
}
rc = 0;
printk(KERN_INFO "%s: WL3501 closed\n", dev->name);
out:
spin_unlock_irqrestore(&this->lock, flags);
return rc;
}
/**
* wl3501_reset - Reset the SUTRO.
* @dev - network device
*
* It is almost the same as wl3501_open(). In fact, we may just wl3501_close()
* and wl3501_open() again, but I wouldn't like to free_irq() when the driver
* is running. It seems to be dangerous.
*/
static int wl3501_reset(struct net_device *dev)
{
struct wl3501_card *this = dev->priv;
int rc = -ENODEV;
wl3501_block_interrupt(this);
if (wl3501_init_firmware(this)) {
printk(KERN_WARNING "%s: Can't initialize Firmware!\n",
dev->name);
/* Free IRQ, and mark IRQ as unused */
free_irq(dev->irq, dev);
goto out;
}
/*
* Queue has to be started only when the Card is Started
*/
netif_stop_queue(dev);
this->adhoc_times = 0;
wl3501_ack_interrupt(this);
wl3501_unblock_interrupt(this);
wl3501_mgmt_scan(this, 100);
dprintk(1, "%s: device reset", dev->name);
rc = 0;
out:
return rc;
}
static void wl3501_tx_timeout(struct net_device *dev)
{
struct wl3501_card *this = dev->priv;
struct net_device_stats *stats = &this->stats;
unsigned long flags;
int rc;
stats->tx_errors++;
spin_lock_irqsave(&this->lock, flags);
rc = wl3501_reset(dev);
spin_unlock_irqrestore(&this->lock, flags);
if (rc)
printk(KERN_ERR "%s: Error %d resetting card on Tx timeout!\n",
dev->name, rc);
else {
dev->trans_start = jiffies;
netif_wake_queue(dev);
}
}
/*
* Return : 0 - OK
* 1 - Could not transmit (dev_queue_xmit will queue it)
* and try to sent it later
*/
static int wl3501_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
int enabled, rc;
struct wl3501_card *this = dev->priv;
unsigned long flags;
spin_lock_irqsave(&this->lock, flags);
enabled = wl3501_block_interrupt(this);
dev->trans_start = jiffies;
rc = wl3501_send_pkt(this, skb->data, skb->len);
if (enabled)
wl3501_unblock_interrupt(this);
if (rc) {
++this->stats.tx_dropped;
netif_stop_queue(dev);
} else {
++this->stats.tx_packets;
this->stats.tx_bytes += skb->len;
kfree_skb(skb);
if (this->tx_buffer_cnt < 2)
netif_stop_queue(dev);
}
spin_unlock_irqrestore(&this->lock, flags);
return rc;
}
static int wl3501_open(struct net_device *dev)
{
int rc = -ENODEV;
struct wl3501_card *this = dev->priv;
unsigned long flags;
dev_link_t *link;
spin_lock_irqsave(&this->lock, flags);
/* Check if the device is in wl3501_dev_list */
for (link = wl3501_dev_list; link; link = link->next)
if (link->priv == dev)
break;
if (!DEV_OK(link))
goto out;
netif_device_attach(dev);
link->open++;
/* Initial WL3501 firmware */
dprintk(1, "%s: Initialize WL3501 firmware...", dev->name);
if (wl3501_init_firmware(this))
goto fail;
/* Initial device variables */
this->adhoc_times = 0;
/* Acknowledge Interrupt, for cleaning last state */
wl3501_ack_interrupt(this);
/* Enable interrupt from card after all */
wl3501_unblock_interrupt(this);
wl3501_mgmt_scan(this, 100);
rc = 0;
dprintk(1, "%s: WL3501 opened", dev->name);
printk(KERN_INFO "%s: Card Name: %s\n"
"%s: Firmware Date: %s\n",
dev->name, this->card_name,
dev->name, this->firmware_date);
out:
spin_unlock_irqrestore(&this->lock, flags);
return rc;
fail:
printk(KERN_WARNING "%s: Can't initialize firmware!\n", dev->name);
goto out;
}
/**
* wl3501_init - "initialize" board
* @dev - network device
*
* We never need to do anything when a wl3501 device is "initialized" by the net
* software, because we only register already-found cards.
*/
static int wl3501_init(struct net_device *dev)
{
return 0;
}
struct net_device_stats *wl3501_get_stats(struct net_device *dev)
{
struct wl3501_card *this = dev->priv;
return &this->stats;
}
struct iw_statistics *wl3501_get_wireless_stats(struct net_device *dev)
{
struct wl3501_card *this = dev->priv;
struct iw_statistics *wstats = &this->wstats;
u32 value; /* size checked: it is u32 */
memset(wstats, 0, sizeof(*wstats));
wstats->status = netif_running(dev);
if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_ICV_ERROR_COUNT,
&value, sizeof(value)))
wstats->discard.code += value;
if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_UNDECRYPTABLE_COUNT,
&value, sizeof(value)))
wstats->discard.code += value;
if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_EXCLUDED_COUNT,
&value, sizeof(value)))
wstats->discard.code += value;
if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_RETRY_COUNT,
&value, sizeof(value)))
wstats->discard.retries = value;
if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_FAILED_COUNT,
&value, sizeof(value)))
wstats->discard.misc += value;
if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_RTS_FAILURE_COUNT,
&value, sizeof(value)))
wstats->discard.misc += value;
if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_ACK_FAILURE_COUNT,
&value, sizeof(value)))
wstats->discard.misc += value;
if (!wl3501_get_mib_value(this, WL3501_MIB_ATTR_FRAME_DUPLICATE_COUNT,
&value, sizeof(value)))
wstats->discard.misc += value;
return wstats;
}
static inline int wl3501_ethtool_ioctl(struct net_device *dev, void *uaddr)
{
u32 ethcmd;
int rc = -EFAULT;
if (copy_from_user(&ethcmd, uaddr, sizeof(ethcmd)))
goto out;
switch (ethcmd) {
case ETHTOOL_GDRVINFO: {
struct ethtool_drvinfo info = { .cmd = ETHTOOL_GDRVINFO, };
strlcpy(info.driver, wl3501_dev_info, sizeof(info.driver));
rc = copy_to_user(uaddr, &info, sizeof(info)) ? -EFAULT : 1;
}
default:
rc = -EOPNOTSUPP;
break;
}
out:
return rc;
}
/**
* wl3501_ioctl - Perform IOCTL call functions
* @dev - network device
* @ifreq - request
* @cmd - command
*
* Perform IOCTL call functions here. Some are privileged operations and the
* effective uid is checked in those cases.
*
* This part is optional. Needed only if you want to run wlu (unix version).
*
* CAUTION: To prevent interrupted by wl3501_interrupt() and timer-based
* wl3501_hard_start_xmit() from other interrupts, this should be run
* single-threaded.
*/
static int wl3501_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
int rc = -ENODEV;
if (netif_device_present(dev)) {
rc = -EOPNOTSUPP;
if (cmd == SIOCETHTOOL)
rc = wl3501_ethtool_ioctl(dev, (void *)rq->ifr_data);
}
return rc;
}
/**
* wl3501_detach - deletes a driver "instance"
* @link - FILL_IN
*
* This deletes a driver "instance". The device is de-registered with Card
* Services. If it has been released, all local data structures are freed.
* Otherwise, the structures will be freed when the device is released.
*/
static void wl3501_detach(dev_link_t *link)
{
dev_link_t **linkp;
/* Locate device structure */
for (linkp = &wl3501_dev_list; *linkp; linkp = &(*linkp)->next)
if (*linkp == link)
break;
if (!*linkp)
goto out;
/* If the device is currently configured and active, we won't actually
* delete it yet. Instead, it is marked so that when the release()
* function is called, that will trigger a proper detach(). */
if (link->state & DEV_CONFIG) {
#ifdef PCMCIA_DEBUG
printk(KERN_DEBUG "wl3501_cs: detach postponed, '%s' "
"still locked\n", link->dev->dev_name);
#endif
link->state |= DEV_STALE_LINK;
goto out;
}
/* Break the link with Card Services */
if (link->handle)
CardServices(DeregisterClient, link->handle);
/* Unlink device structure, free pieces */
*linkp = link->next;
if (link->priv)
kfree(link->priv);
kfree(link);
out:
return;
}
/**
* wl3501_flush_stale_links - Remove zombie instances
*
* Remove zombie instances (card removed, detach pending)
*/
static void wl3501_flush_stale_links(void)
{
dev_link_t *link, *next;
for (link = wl3501_dev_list; link; link = next) {
next = link->next;
if (link->state & DEV_STALE_LINK)
wl3501_detach(link);
}
}
static int wl3501_get_name(struct net_device *dev, struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
strlcpy(wrqu->name, "IEEE 802.11-DS", sizeof(wrqu->name));
return 0;
}
static int wl3501_set_freq(struct net_device *dev, struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct wl3501_card *this = dev->priv;
int channel = wrqu->freq.m;
int rc = -EINVAL;
if (iw_valid_channel(this->reg_domain, channel)) {
this->chan = channel;
rc = wl3501_reset(dev);
}
return rc;
}
static int wl3501_get_freq(struct net_device *dev, struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct wl3501_card *this = dev->priv;
wrqu->freq.m = wl3501_chan2freq[this->chan - 1] * 100000;
wrqu->freq.e = 1;
return 0;
}
static int wl3501_set_mode(struct net_device *dev, struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
int rc = -EINVAL;
if (wrqu->mode == IW_MODE_INFRA ||
wrqu->mode == IW_MODE_ADHOC ||
wrqu->mode == IW_MODE_AUTO) {
struct wl3501_card *this = dev->priv;
this->net_type = wrqu->mode;
rc = wl3501_reset(dev);
}
return rc;
}
static int wl3501_get_mode(struct net_device *dev, struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct wl3501_card *this = dev->priv;
wrqu->mode = this->net_type;
return 0;
}
static int wl3501_get_sens(struct net_device *dev, struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct wl3501_card *this = dev->priv;
wrqu->sens.value = this->rssi;
wrqu->sens.disabled = !wrqu->sens.value;
wrqu->sens.fixed = 1;
return 0;
}
static int wl3501_get_range(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct iw_range *range = (struct iw_range *)extra;
/* Set the length (very important for backward compatibility) */
wrqu->data.length = sizeof(*range);
/* Set all the info we don't care or don't know about to zero */
memset(range, 0, sizeof(*range));
/* Set the Wireless Extension versions */
range->we_version_compiled = WIRELESS_EXT;
range->we_version_source = 1;
range->throughput = 2 * 1000 * 1000; /* ~2 Mb/s */
/* FIXME: study the code to fill in more fields... */
return 0;
}
static int wl3501_set_wap(struct net_device *dev, struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct wl3501_card *this = dev->priv;
static const u8 bcast[ETH_ALEN] = { 255, 255, 255, 255, 255, 255 };
int rc = -EINVAL;
/* FIXME: we support other ARPHRDs...*/
if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
goto out;
if (!memcmp(bcast, wrqu->ap_addr.sa_data, ETH_ALEN)) {
/* FIXME: rescan? */
} else
memcpy(this->bssid, wrqu->ap_addr.sa_data, ETH_ALEN);
/* FIXME: rescan? deassoc & scan? */
rc = 0;
out:
return rc;
}
static int wl3501_get_wap(struct net_device *dev, struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct wl3501_card *this = dev->priv;
wrqu->ap_addr.sa_family = ARPHRD_ETHER;
memcpy(wrqu->ap_addr.sa_data, this->bssid, ETH_ALEN);
return 0;
}
static int wl3501_set_essid(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct wl3501_card *this = dev->priv;
int rc = 0;
if (wrqu->data.flags) {
strlcpy(this->essid + 2, extra, min_t(u16, wrqu->data.length,
IW_ESSID_MAX_SIZE));
rc = wl3501_reset(dev);
}
return rc;
}
static int wl3501_get_essid(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct wl3501_card *this = dev->priv;
unsigned long flags;
spin_lock_irqsave(&this->lock, flags);
wrqu->essid.flags = 1;
wrqu->essid.length = IW_ESSID_MAX_SIZE;
strlcpy(extra, this->essid + 2, IW_ESSID_MAX_SIZE);
spin_unlock_irqrestore(&this->lock, flags);
return 0;
}
static int wl3501_set_nick(struct net_device *dev, struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct wl3501_card *this = dev->priv;
if (wrqu->data.length > sizeof(this->nick))
return -E2BIG;
strlcpy(this->nick, extra, wrqu->data.length);
return 0;
}
static int wl3501_get_nick(struct net_device *dev, struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct wl3501_card *this = dev->priv;
strlcpy(extra, this->nick, 32);
wrqu->data.length = strlen(extra);
return 0;
}
static int wl3501_get_rate(struct net_device *dev, struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
/*
* FIXME: have to see from where to get this info, perhaps this card
* works at 1 Mbit/s too... for now leave at 2 Mbit/s that is the most
* common with the Planet Access Points. -acme
*/
wrqu->bitrate.value = 2000000;
wrqu->bitrate.fixed = 1;
return 0;
}
static int wl3501_get_rts_threshold(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
u16 threshold; /* size checked: it is u16 */
struct wl3501_card *this = dev->priv;
int rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_RTS_THRESHOLD,
&threshold, sizeof(threshold));
if (!rc) {
wrqu->rts.value = threshold;
wrqu->rts.disabled = threshold >= 2347;
wrqu->rts.fixed = 1;
}
return rc;
}
static int wl3501_get_frag_threshold(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
u16 threshold; /* size checked: it is u16 */
struct wl3501_card *this = dev->priv;
int rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_FRAG_THRESHOLD,
&threshold, sizeof(threshold));
if (!rc) {
wrqu->frag.value = threshold;
wrqu->frag.disabled = threshold >= 2346;
wrqu->frag.fixed = 1;
}
return rc;
}
static int wl3501_get_txpow(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
u16 txpow;
struct wl3501_card *this = dev->priv;
int rc = wl3501_get_mib_value(this,
WL3501_MIB_ATTR_CURRENT_TX_PWR_LEVEL,
&txpow, sizeof(txpow));
if (!rc) {
wrqu->txpower.value = txpow;
wrqu->txpower.disabled = 0;
/*
* From the MIB values I think this can be configurable,
* as it lists several tx power levels -acme
*/
wrqu->txpower.fixed = 0;
wrqu->txpower.flags = IW_TXPOW_MWATT;
}
return rc;
}
static int wl3501_get_retry(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
u8 retry; /* size checked: it is u8 */
struct wl3501_card *this = dev->priv;
int rc = wl3501_get_mib_value(this,
WL3501_MIB_ATTR_LONG_RETRY_LIMIT,
&retry, sizeof(retry));
if (rc)
goto out;
if (wrqu->retry.flags & IW_RETRY_MAX) {
wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
goto set_value;
}
rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_SHORT_RETRY_LIMIT,
&retry, sizeof(retry));
if (rc)
goto out;
wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
set_value:
wrqu->retry.value = retry;
wrqu->retry.disabled = 0;
out:
return rc;
}
static int wl3501_get_encode(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
u8 implemented, restricted, keys[100], len_keys, tocopy;
struct wl3501_card *this = dev->priv;
int rc = wl3501_get_mib_value(this,
WL3501_MIB_ATTR_PRIV_OPT_IMPLEMENTED,
&implemented, sizeof(implemented));
if (rc)
goto out;
if (!implemented) {
wrqu->encoding.flags = IW_ENCODE_DISABLED;
goto out;
}
rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_EXCLUDE_UNENCRYPTED,
&restricted, sizeof(restricted));
if (rc)
goto out;
wrqu->encoding.flags = restricted ? IW_ENCODE_RESTRICTED :
IW_ENCODE_OPEN;
rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_KEY_MAPPINGS_LEN,
&len_keys, sizeof(len_keys));
if (rc)
goto out;
rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_WEP_KEY_MAPPINGS,
keys, len_keys);
if (rc)
goto out;
tocopy = min_t(u8, len_keys, wrqu->encoding.length);
tocopy = min_t(u8, tocopy, 100);
wrqu->encoding.length = tocopy;
memset(extra, 0, tocopy);
memcpy(extra, keys, tocopy);
out:
return rc;
}
static int wl3501_get_power(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
u8 pwr_state;
struct wl3501_card *this = dev->priv;
int rc = wl3501_get_mib_value(this,
WL3501_MIB_ATTR_CURRENT_PWR_STATE,
&pwr_state, sizeof(pwr_state));
if (rc)
goto out;
wrqu->power.disabled = !pwr_state;
wrqu->power.flags = IW_POWER_ON;
out:
return rc;
}
static const iw_handler wl3501_handler[] = {
[SIOCGIWNAME - SIOCIWFIRST] = wl3501_get_name,
[SIOCSIWFREQ - SIOCIWFIRST] = wl3501_set_freq,
[SIOCGIWFREQ - SIOCIWFIRST] = wl3501_get_freq,
[SIOCSIWMODE - SIOCIWFIRST] = wl3501_set_mode,
[SIOCGIWMODE - SIOCIWFIRST] = wl3501_get_mode,
[SIOCGIWSENS - SIOCIWFIRST] = wl3501_get_sens,
[SIOCGIWRANGE - SIOCIWFIRST] = wl3501_get_range,
[SIOCSIWSPY - SIOCIWFIRST] = iw_handler_set_spy,
[SIOCGIWSPY - SIOCIWFIRST] = iw_handler_get_spy,
[SIOCSIWTHRSPY - SIOCIWFIRST] = iw_handler_set_thrspy,
[SIOCGIWTHRSPY - SIOCIWFIRST] = iw_handler_get_thrspy,
[SIOCSIWAP - SIOCIWFIRST] = wl3501_set_wap,
[SIOCGIWAP - SIOCIWFIRST] = wl3501_get_wap,
[SIOCSIWESSID - SIOCIWFIRST] = wl3501_set_essid,
[SIOCGIWESSID - SIOCIWFIRST] = wl3501_get_essid,
[SIOCSIWNICKN - SIOCIWFIRST] = wl3501_set_nick,
[SIOCGIWNICKN - SIOCIWFIRST] = wl3501_get_nick,
[SIOCGIWRATE - SIOCIWFIRST] = wl3501_get_rate,
[SIOCGIWRTS - SIOCIWFIRST] = wl3501_get_rts_threshold,
[SIOCGIWFRAG - SIOCIWFIRST] = wl3501_get_frag_threshold,
[SIOCGIWTXPOW - SIOCIWFIRST] = wl3501_get_txpow,
[SIOCGIWRETRY - SIOCIWFIRST] = wl3501_get_retry,
[SIOCGIWENCODE - SIOCIWFIRST] = wl3501_get_encode,
[SIOCGIWPOWER - SIOCIWFIRST] = wl3501_get_power,
};
static const struct iw_handler_def wl3501_handler_def = {
.num_standard = sizeof(wl3501_handler) / sizeof(iw_handler),
.standard = (iw_handler *)wl3501_handler,
.spy_offset = offsetof(struct wl3501_card, spy_data),
};
/**
* wl3501_attach - creates an "instance" of the driver
*
* Creates an "instance" of the driver, allocating local data structures for
* one device. The device is registered with Card Services.
*
* The dev_link structure is initialized, but we don't actually configure the
* card at this point -- we wait until we receive a card insertion event.
*/
static dev_link_t *wl3501_attach(void)
{
client_reg_t client_reg;
dev_link_t *link;
struct net_device *dev;
int ret, i;
wl3501_flush_stale_links();
/* Initialize the dev_link_t structure */
link = kmalloc(sizeof(*link), GFP_KERNEL);
if (!link)
goto out;
memset(link, 0, sizeof(struct dev_link_t));
init_timer(&link->release);
link->release.function = wl3501_release;
link->release.data = (unsigned long)link;
/* The io structure describes IO port mapping */
link->io.NumPorts1 = 16;
link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
link->io.IOAddrLines = 5;
/* Interrupt setup */
link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
link->irq.IRQInfo2 = wl3501_irq_mask;
if (wl3501_irq_list[0] != -1)
for (i = 0; i < 4; i++)
link->irq.IRQInfo2 |= 1 << wl3501_irq_list[i];
link->irq.Handler = wl3501_interrupt;
/* General socket configuration */
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.Vcc = 50;
link->conf.IntType = INT_MEMORY_AND_IO;
link->conf.ConfigIndex = 1;
link->conf.Present = PRESENT_OPTION;
dev = alloc_etherdev(sizeof(struct wl3501_card));
if (!dev)
goto out_link;
dev->init = wl3501_init;
dev->open = wl3501_open;
dev->stop = wl3501_close;
dev->hard_start_xmit = wl3501_hard_start_xmit;
dev->tx_timeout = wl3501_tx_timeout;
dev->watchdog_timeo = 5 * HZ;
dev->get_stats = wl3501_get_stats;
dev->get_wireless_stats = wl3501_get_wireless_stats;
dev->do_ioctl = wl3501_ioctl;
dev->wireless_handlers = (struct iw_handler_def *)&wl3501_handler_def;
netif_stop_queue(dev);
link->priv = link->irq.Instance = dev;
/* Register with Card Services */
link->next = wl3501_dev_list;
wl3501_dev_list = link;
client_reg.dev_info = &wl3501_dev_info;
client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
client_reg.EventMask = CS_EVENT_CARD_INSERTION |
CS_EVENT_RESET_PHYSICAL |
CS_EVENT_CARD_RESET |
CS_EVENT_CARD_REMOVAL |
CS_EVENT_PM_SUSPEND |
CS_EVENT_PM_RESUME;
client_reg.event_handler = wl3501_event;
client_reg.Version = 0x0210;
client_reg.event_callback_args.client_data = link;
ret = CardServices(RegisterClient, &link->handle, &client_reg);
if (ret) {
cs_error(link->handle, RegisterClient, ret);
wl3501_detach(link);
link = NULL;
}
out:
return link;
out_link:
kfree(link);
link = NULL;
goto out;
}
#define CS_CHECK(fn, args...) \
while ((last_ret = CardServices(last_fn = (fn), args)) != 0) goto cs_failed
/**
* wl3501_config - configure the PCMCIA socket and make eth device available
* @link - FILL_IN
*
* wl3501_config() is scheduled to run after a CARD_INSERTION event is
* received, to configure the PCMCIA socket, and to make the ethernet device
* available to the system.
*/
static void wl3501_config(dev_link_t *link)
{
tuple_t tuple;
cisparse_t parse;
client_handle_t handle = link->handle;
struct net_device *dev = link->priv;
int i = 0, j, last_fn, last_ret;
unsigned char bf[64];
struct wl3501_card *this;
/* This reads the card's CONFIG tuple to find its config registers. */
tuple.Attributes = 0;
tuple.DesiredTuple = CISTPL_CONFIG;
CS_CHECK(GetFirstTuple, handle, &tuple);
tuple.TupleData = bf;
tuple.TupleDataMax = sizeof(bf);
tuple.TupleOffset = 0;
CS_CHECK(GetTupleData, handle, &tuple);
CS_CHECK(ParseTuple, handle, &tuple, &parse);
link->conf.ConfigBase = parse.config.base;
link->conf.Present = parse.config.rmask[0];
/* Configure card */
link->state |= DEV_CONFIG;
/* Try allocating IO ports. This tries a few fixed addresses. If you
* want, you can also read the card's config table to pick addresses --
* see the serial driver for an example. */
for (j = 0x280; j < 0x400; j += 0x20) {
/* The '^0x300' is so that we probe 0x300-0x3ff first, then
* 0x200-0x2ff, and so on, because this seems safer */
link->io.BasePort1 = j;
link->io.BasePort2 = link->io.BasePort1 + 0x10;
i = CardServices(RequestIO, link->handle, &link->io);
if (i == CS_SUCCESS)
break;
}
if (i != CS_SUCCESS) {
cs_error(link->handle, RequestIO, i);
goto failed;
}
/* Now allocate an interrupt line. Note that this does not actually
* assign a handler to the interrupt. */
CS_CHECK(RequestIRQ, link->handle, &link->irq);
/* This actually configures the PCMCIA socket -- setting up the I/O
* windows and the interrupt mapping. */
CS_CHECK(RequestConfiguration, link->handle, &link->conf);
dev->irq = link->irq.AssignedIRQ;
dev->base_addr = link->io.BasePort1;
if (register_netdev(dev)) {
printk(KERN_NOTICE "wl3501_cs: register_netdev() failed\n");
goto failed;
}
SET_MODULE_OWNER(dev);
this = dev->priv;
/*
* At this point, the dev_node_t structure(s) should be initialized and
* arranged in a linked list at link->dev.
*/
link->dev = &this->node;
link->state &= ~DEV_CONFIG_PENDING;
this->base_addr = dev->base_addr;
if (!wl3501_get_flash_mac_addr(this)) {
printk(KERN_WARNING "%s: Cant read MAC addr in flash ROM?\n",
dev->name);
goto failed;
}
strcpy(this->node.dev_name, dev->name);
/* print probe information */
printk(KERN_INFO "%s: wl3501 @ 0x%3.3x, IRQ %d, MAC addr in flash ROM:",
dev->name, this->base_addr, (int)dev->irq);
for (i = 0; i < 6; i++) {
dev->dev_addr[i] = ((char *)&this->mac_addr)[i];
printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]);
}
printk("\n");
/*
* Initialize card parameters - added by jss
*/
this->net_type = IW_MODE_INFRA;
this->bss_cnt = 0;
this->join_sta_bss = 0;
this->adhoc_times = 0;
this->essid[0] = 0;
this->essid[1] = 3;
this->essid[2] = 'A';
this->essid[3] = 'N';
this->essid[4] = 'Y';
this->card_name[0] = '\0';
this->firmware_date[0] = '\0';
this->rssi = 255;
this->chan = iw_default_channel(this->reg_domain);
strlcpy(this->nick, "Planet WL3501", sizeof(this->nick));
spin_lock_init(&this->lock);
init_waitqueue_head(&this->wait);
netif_start_queue(dev);
goto out;
cs_failed:
cs_error(link->handle, last_fn, last_ret);
failed:
wl3501_release((unsigned long)link);
out:
return;
}
/**
* wl3501_release - unregister the net, release PCMCIA configuration
* @arg - link
*
* After a card is removed, wl3501_release() will unregister the net device,
* and release the PCMCIA configuration. If the device is still open, this
* will be postponed until it is closed.
*/
static void wl3501_release(unsigned long arg)
{
dev_link_t *link = (dev_link_t *)arg;
struct net_device *dev = link->priv;
/* If the device is currently in use, we won't release until it is
* actually closed. */
if (link->open) {
dprintk(1, "release postponed, '%s' still open",
link->dev->dev_name);
link->state |= DEV_STALE_CONFIG;
goto out;
}
/* Unlink the device chain */
if (link->dev) {
unregister_netdev(dev);
link->dev = NULL;
}
/* Don't bother checking to see if these succeed or not */
CardServices(ReleaseConfiguration, link->handle);
CardServices(ReleaseIO, link->handle, &link->io);
CardServices(ReleaseIRQ, link->handle, &link->irq);
link->state &= ~DEV_CONFIG;
if (link->state & DEV_STALE_LINK)
wl3501_detach(link);
out:
return;
}
/**
* wl3501_event - The card status event handler
* @event - event
* @pri - priority
* @args - arguments for this event
*
* The card status event handler. Mostly, this schedules other stuff to run
* after an event is received. A CARD_REMOVAL event also sets some flags to
* discourage the net drivers from trying to talk to the card any more.
*
* When a CARD_REMOVAL event is received, we immediately set a flag to block
* future accesses to this device. All the functions that actually access the
* device should check this flag to make sure the card is still present.
*/
static int wl3501_event(event_t event, int pri, event_callback_args_t *args)
{
dev_link_t *link = args->client_data;
struct net_device *dev = link->priv;
switch (event) {
case CS_EVENT_CARD_REMOVAL:
link->state &= ~DEV_PRESENT;
if (link->state & DEV_CONFIG) {
while (link->open > 0)
wl3501_close(dev);
netif_device_detach(dev);
link->release.expires = jiffies +
WL3501_RELEASE_TIMEOUT;
add_timer(&link->release);
}
break;
case CS_EVENT_CARD_INSERTION:
link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
wl3501_config(link);
break;
case CS_EVENT_PM_SUSPEND:
link->state |= DEV_SUSPEND;
wl3501_pwr_mgmt(dev->priv, WL3501_SUSPEND);
/* Fall through... */
case CS_EVENT_RESET_PHYSICAL:
if (link->state & DEV_CONFIG) {
if (link->open)
netif_device_detach(dev);
CardServices(ReleaseConfiguration, link->handle);
}
break;
case CS_EVENT_PM_RESUME:
link->state &= ~DEV_SUSPEND;
wl3501_pwr_mgmt(dev->priv, WL3501_RESUME);
/* Fall through... */
case CS_EVENT_CARD_RESET:
if (link->state & DEV_CONFIG) {
CardServices(RequestConfiguration, link->handle,
&link->conf);
if (link->open) {
wl3501_reset(dev);
netif_device_attach(dev);
}
}
break;
}
return 0;
}
static struct pcmcia_driver wl3501_driver = {
.owner = THIS_MODULE,
.drv = {
.name = "wl3501_cs",
},
.attach = wl3501_attach,
.detach = wl3501_detach,
};
static int __init wl3501_init_module(void)
{
servinfo_t serv;
dprintk(0, ": loading");
CardServices(GetCardServicesInfo, &serv);
if (serv.Revision != CS_RELEASE_CODE) {
printk(KERN_NOTICE
"wl3501_cs: Card Services release does not match!\n"
"Compiled with 0x%x, but current is 0x%lx\n",
CS_RELEASE_CODE, (unsigned long)serv.Revision);
/* return -1; */
}
pcmcia_register_driver(&wl3501_driver);
return 0;
}
static void __exit wl3501_exit_module(void)
{
dprintk(0, ": unloading");
pcmcia_unregister_driver(&wl3501_driver);
while (wl3501_dev_list) {
del_timer(&wl3501_dev_list->release);
/* Mark the device as non-existing to minimize calls to card */
wl3501_dev_list->state &= ~DEV_PRESENT;
if (wl3501_dev_list->state & DEV_CONFIG)
wl3501_release((unsigned long)wl3501_dev_list);
wl3501_detach(wl3501_dev_list);
}
}
module_init(wl3501_init_module);
module_exit(wl3501_exit_module);
MODULE_PARM(wl3501_irq_mask, "i");
MODULE_PARM(wl3501_irq_list, "1-4i");
MODULE_AUTHOR("Fox Chen <mhchen@golf.ccl.itri.org.tw>, "
"Arnaldo Carvalho de Melo <acme@conectiva.com.br>,"
"Gustavo Niemeyer <niemeyer@conectiva.com>");
MODULE_DESCRIPTION("Planet wl3501 wireless driver");
MODULE_LICENSE("GPL");
...@@ -281,6 +281,8 @@ struct ethtool_stats { ...@@ -281,6 +281,8 @@ struct ethtool_stats {
#define ETHTOOL_GSTRINGS 0x0000001b /* get specified string set */ #define ETHTOOL_GSTRINGS 0x0000001b /* get specified string set */
#define ETHTOOL_PHYS_ID 0x0000001c /* identify the NIC */ #define ETHTOOL_PHYS_ID 0x0000001c /* identify the NIC */
#define ETHTOOL_GSTATS 0x0000001d /* get NIC-specific statistics */ #define ETHTOOL_GSTATS 0x0000001d /* get NIC-specific statistics */
#define ETHTOOL_GTSO 0x0000001e /* Get TSO enable (ethtool_value) */
#define ETHTOOL_STSO 0x0000001f /* Set TSO enable (ethtool_value) */
/* compatibility with older code */ /* compatibility with older code */
#define SPARC_ETH_GSET ETHTOOL_GSET #define SPARC_ETH_GSET ETHTOOL_GSET
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment