diff -Nru a/Documentation/networking/ifenslave.c b/Documentation/networking/ifenslave.c --- a/Documentation/networking/ifenslave.c Wed Aug 27 18:30:33 2003 +++ b/Documentation/networking/ifenslave.c Wed Aug 27 18:30:33 2003 @@ -140,6 +140,7 @@ #include #include #include +#include #include #include #include diff -Nru a/drivers/net/3c501.c b/drivers/net/3c501.c --- a/drivers/net/3c501.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/3c501.c Wed Aug 27 18:30:33 2003 @@ -148,7 +148,7 @@ static int el1_close(struct net_device *dev); static struct net_device_stats *el1_get_stats(struct net_device *dev); static void set_multicast_list(struct net_device *dev); -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); +static struct ethtool_ops netdev_ethtool_ops; #define EL1_IO_EXTENT 16 @@ -388,7 +388,7 @@ dev->stop = &el1_close; dev->get_stats = &el1_get_stats; dev->set_multicast_list = &set_multicast_list; - dev->do_ioctl = netdev_ioctl; + dev->ethtool_ops = &netdev_ethtool_ops; /* * Setup the generic properties @@ -939,86 +939,31 @@ } } -/** - * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls - * @dev: network interface on which out-of-band action is to be performed - * @useraddr: userspace address to which data is to be read and returned - * - * Process the various commands of the SIOCETHTOOL interface. - */ -static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; - - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ - - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } - - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = debug; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - debug = edata.data; - return 0; - } - - default: - break; - } - - return -EOPNOTSUPP; + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + sprintf(info->bus_info, "ISA 0x%lx", dev->base_addr); } -/** - * netdev_ioctl: Handle network interface ioctls - * @dev: network interface on which out-of-band action is to be performed - * @rq: user request data - * @cmd: command issued by user - * - * Process the various out-of-band ioctls passed to this driver. - */ - -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) +static u32 netdev_get_msglevel(struct net_device *dev) { - int rc = 0; + return debug; +} - switch (cmd) { - case SIOCETHTOOL: - rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - break; +static void netdev_set_msglevel(struct net_device *dev, u32 level) +{ + debug = level; +} - default: - rc = -EOPNOTSUPP; - break; - } +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, + .get_msglevel = netdev_get_msglevel, + .set_msglevel = netdev_set_msglevel, +}; - return rc; -} - #ifdef MODULE static struct net_device dev_3c501 = { diff -Nru a/drivers/net/3c503.c b/drivers/net/3c503.c --- a/drivers/net/3c503.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/3c503.c Wed Aug 27 18:30:33 2003 @@ -82,7 +82,7 @@ int ring_offset); static void el2_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page); -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); +static struct ethtool_ops netdev_ethtool_ops; /* This routine probes for a memory-mapped 3c503 board by looking for @@ -310,7 +310,7 @@ dev->open = &el2_open; dev->stop = &el2_close; - dev->do_ioctl = &netdev_ioctl; + dev->ethtool_ops = &netdev_ethtool_ops; if (dev->mem_start) printk("%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n", @@ -619,69 +619,18 @@ return; } -/** - * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls - * @dev: network interface on which out-of-band action is to be performed - * @useraddr: userspace address to which data is to be read and returned - * - * Process the various commands of the SIOCETHTOOL interface. - */ -static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; - - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ - - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } - - default: - break; - } - - return -EOPNOTSUPP; + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + sprintf(info->bus_info, "ISA 0x%lx", dev->base_addr); } -/** - * netdev_ioctl: Handle network interface ioctls - * @dev: network interface on which out-of-band action is to be performed - * @rq: user request data - * @cmd: command issued by user - * - * Process the various out-of-band ioctls passed to this driver. - */ - -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) -{ - int rc = 0; - - switch (cmd) { - case SIOCETHTOOL: - rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - break; - - default: - rc = -EOPNOTSUPP; - break; - } - - return rc; -} - +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +}; #ifdef MODULE #define MAX_EL2_CARDS 4 /* Max number of EL2 cards per module */ diff -Nru a/drivers/net/3c505.c b/drivers/net/3c505.c --- a/drivers/net/3c505.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/3c505.c Wed Aug 27 18:30:33 2003 @@ -1270,86 +1270,30 @@ } } -/** - * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls - * @dev: network interface on which out-of-band action is to be performed - * @useraddr: userspace address to which data is to be read and returned - * - * Process the various commands of the SIOCETHTOOL interface. - */ -static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; - - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ - - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } - - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = debug; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - debug = edata.data; - return 0; - } - - default: - break; - } - - return -EOPNOTSUPP; + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + sprintf(info->bus_info, "ISA 0x%lx", dev->base_addr); } -/** - * netdev_ioctl: Handle network interface ioctls - * @dev: network interface on which out-of-band action is to be performed - * @rq: user request data - * @cmd: command issued by user - * - * Process the various out-of-band ioctls passed to this driver. - */ - -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) +static u32 netdev_get_msglevel(struct net_device *dev) { - int rc = 0; - - switch (cmd) { - case SIOCETHTOOL: - rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - break; - - default: - rc = -EOPNOTSUPP; - break; - } + return debug; +} - return rc; +static void netdev_set_msglevel(struct net_device *dev, u32 level) +{ + debug = level; } - + +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, + .get_msglevel = netdev_get_msglevel, + .set_msglevel = netdev_set_msglevel, +}; /****************************************************** * @@ -1371,7 +1315,7 @@ dev->tx_timeout = elp_timeout; /* local */ dev->watchdog_timeo = 10*HZ; dev->set_multicast_list = elp_set_mc_list; /* local */ - dev->do_ioctl = netdev_ioctl; /* local */ + dev->ethtool_ops = &netdev_ethtool_ops; /* local */ /* Setup the generic properties */ ether_setup(dev); diff -Nru a/drivers/net/3c507.c b/drivers/net/3c507.c --- a/drivers/net/3c507.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/3c507.c Wed Aug 27 18:30:33 2003 @@ -305,7 +305,7 @@ static void hardware_send_packet(struct net_device *dev, void *buf, short length, short pad); static void init_82586_mem(struct net_device *dev); -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); +static struct ethtool_ops netdev_ethtool_ops; /* Check for a network adaptor of this type, and return '0' iff one exists. @@ -437,7 +437,7 @@ dev->get_stats = el16_get_stats; dev->tx_timeout = el16_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; - dev->do_ioctl = netdev_ioctl; + dev->ethtool_ops = &netdev_ethtool_ops; ether_setup(dev); /* Generic ethernet behaviour */ @@ -879,86 +879,29 @@ lp->rx_tail = rx_tail; } -/** - * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls - * @dev: network interface on which out-of-band action is to be performed - * @useraddr: userspace address to which data is to be read and returned - * - * Process the various commands of the SIOCETHTOOL interface. - */ - -static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) -{ - u32 ethcmd; - - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ - - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } - - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = debug; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - debug = edata.data; - return 0; - } - - default: - break; - } - - return -EOPNOTSUPP; +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + sprintf(info->bus_info, "ISA 0x%lx", dev->base_addr); } -/** - * netdev_ioctl: Handle network interface ioctls - * @dev: network interface on which out-of-band action is to be performed - * @rq: user request data - * @cmd: command issued by user - * - * Process the various out-of-band ioctls passed to this driver. - */ - -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) +static u32 netdev_get_msglevel(struct net_device *dev) { - int rc = 0; - - switch (cmd) { - case SIOCETHTOOL: - rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - break; - - default: - rc = -EOPNOTSUPP; - break; - } + return debug; +} - return rc; +static void netdev_set_msglevel(struct net_device *dev, u32 level) +{ + debug = level; } - + +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, + .get_msglevel = netdev_get_msglevel, + .set_msglevel = netdev_set_msglevel, +}; #ifdef MODULE static struct net_device dev_3c507; diff -Nru a/drivers/net/3c515.c b/drivers/net/3c515.c --- a/drivers/net/3c515.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/3c515.c Wed Aug 27 18:30:33 2003 @@ -406,7 +406,7 @@ static void update_stats(int addr, struct net_device *dev); static struct net_device_stats *corkscrew_get_stats(struct net_device *dev); static void set_rx_mode(struct net_device *dev); -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); +static struct ethtool_ops netdev_ethtool_ops; /* @@ -736,7 +736,7 @@ dev->stop = &corkscrew_close; dev->get_stats = &corkscrew_get_stats; dev->set_multicast_list = &set_rx_mode; - dev->do_ioctl = netdev_ioctl; + dev->ethtool_ops = &netdev_ethtool_ops; return 0; } @@ -1608,86 +1608,30 @@ outw(new_mode, ioaddr + EL3_CMD); } -/** - * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls - * @dev: network interface on which out-of-band action is to be performed - * @useraddr: userspace address to which data is to be read and returned - * - * Process the various commands of the SIOCETHTOOL interface. - */ - -static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; - - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ - - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } - - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = corkscrew_debug; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - corkscrew_debug = edata.data; - return 0; - } - - default: - break; - } - - return -EOPNOTSUPP; + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + sprintf(info->bus_info, "ISA 0x%lx", dev->base_addr); } -/** - * netdev_ioctl: Handle network interface ioctls - * @dev: network interface on which out-of-band action is to be performed - * @rq: user request data - * @cmd: command issued by user - * - * Process the various out-of-band ioctls passed to this driver. - */ - -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) +static u32 netdev_get_msglevel(struct net_device *dev) { - int rc = 0; + return debug; +} - switch (cmd) { - case SIOCETHTOOL: - rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - break; +static void netdev_set_msglevel(struct net_device *dev, u32 level) +{ + debug = level; +} - default: - rc = -EOPNOTSUPP; - break; - } +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, + .get_msglevel = netdev_get_msglevel, + .set_msglevel = netdev_set_msglevel, +}; - return rc; -} - #ifdef MODULE void cleanup_module(void) diff -Nru a/drivers/net/3c523.c b/drivers/net/3c523.c --- a/drivers/net/3c523.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/3c523.c Wed Aug 27 18:30:33 2003 @@ -190,7 +190,7 @@ #ifdef ELMC_MULTICAST static void set_multicast_list(struct net_device *dev); #endif -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); +static struct ethtool_ops netdev_ethtool_ops; /* helper-functions */ static int init586(struct net_device *dev); @@ -573,7 +573,7 @@ #else dev->set_multicast_list = NULL; #endif - dev->do_ioctl = netdev_ioctl; + dev->ethtool_ops = &netdev_ethtool_ops; ether_setup(dev); @@ -1228,70 +1228,17 @@ } #endif -/** - * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls - * @dev: network interface on which out-of-band action is to be performed - * @useraddr: userspace address to which data is to be read and returned - * - * Process the various commands of the SIOCETHTOOL interface. - */ - -static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; - - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ - - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - sprintf(info.bus_info, "MCA 0x%lx", dev->base_addr); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } - - default: - break; - } - - return -EOPNOTSUPP; + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + sprintf(info->bus_info, "MCA 0x%lx", dev->base_addr); } -/** - * netdev_ioctl: Handle network interface ioctls - * @dev: network interface on which out-of-band action is to be performed - * @rq: user request data - * @cmd: command issued by user - * - * Process the various out-of-band ioctls passed to this driver. - */ - -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) -{ - int rc = 0; - - switch (cmd) { - case SIOCETHTOOL: - rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - break; - - default: - rc = -EOPNOTSUPP; - break; - } - - return rc; -} - -/*************************************************************************/ +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +}; #ifdef MODULE diff -Nru a/drivers/net/3c527.c b/drivers/net/3c527.c --- a/drivers/net/3c527.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/3c527.c Wed Aug 27 18:30:33 2003 @@ -220,7 +220,7 @@ static struct net_device_stats *mc32_get_stats(struct net_device *dev); static void mc32_set_multicast_list(struct net_device *dev); static void mc32_reset_multicast_list(struct net_device *dev); -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); +static struct ethtool_ops netdev_ethtool_ops; /** * mc32_probe - Search for supported boards @@ -510,7 +510,7 @@ dev->set_multicast_list = mc32_set_multicast_list; dev->tx_timeout = mc32_timeout; dev->watchdog_timeo = HZ*5; /* Board does all the work */ - dev->do_ioctl = netdev_ioctl; + dev->ethtool_ops = &netdev_ethtool_ops; lp->xceiver_state = HALTED; @@ -1658,86 +1658,30 @@ do_mc32_set_multicast_list(dev,1); } -/** - * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls - * @dev: network interface on which out-of-band action is to be performed - * @useraddr: userspace address to which data is to be read and returned - * - * Process the various commands of the SIOCETHTOOL interface. - */ - -static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; - - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ - - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - sprintf(info.bus_info, "MCA 0x%lx", dev->base_addr); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } - - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = mc32_debug; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - mc32_debug = edata.data; - return 0; - } - - default: - break; - } - - return -EOPNOTSUPP; + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + sprintf(info->bus_info, "MCA 0x%lx", dev->base_addr); } -/** - * netdev_ioctl: Handle network interface ioctls - * @dev: network interface on which out-of-band action is to be performed - * @rq: user request data - * @cmd: command issued by user - * - * Process the various out-of-band ioctls passed to this driver. - */ - -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) +static u32 netdev_get_msglevel(struct net_device *dev) { - int rc = 0; + return mc32_debug; +} - switch (cmd) { - case SIOCETHTOOL: - rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - break; +static void netdev_set_msglevel(struct net_device *dev, u32 level) +{ + mc32_debug = level; +} - default: - rc = -EOPNOTSUPP; - break; - } +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, + .get_msglevel = netdev_get_msglevel, + .set_msglevel = netdev_set_msglevel, +}; - return rc; -} - #ifdef MODULE static struct net_device this_device; diff -Nru a/drivers/net/3c59x.c b/drivers/net/3c59x.c --- a/drivers/net/3c59x.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/3c59x.c Wed Aug 27 18:30:33 2003 @@ -866,6 +866,7 @@ static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static void vortex_tx_timeout(struct net_device *dev); static void acpi_set_WOL(struct net_device *dev); +static struct ethtool_ops vortex_ethtool_ops; /* This driver uses 'options' to pass the media type, full-duplex flag, etc. */ /* Option count limit only -- unlimited interfaces are supported. */ @@ -1330,6 +1331,7 @@ dev->stop = vortex_close; dev->get_stats = vortex_get_stats; dev->do_ioctl = vortex_ioctl; + dev->ethtool_ops = &vortex_ethtool_ops; dev->set_multicast_list = set_rx_mode; dev->tx_timeout = vortex_tx_timeout; dev->watchdog_timeo = (watchdog * HZ) / 1000; @@ -2690,35 +2692,24 @@ return; } - -static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) +static void vortex_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { struct vortex_private *vp = dev->priv; - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strcpy(info.driver, DRV_NAME); - strcpy(info.version, DRV_VERSION); - if (vp->pdev) - strcpy(info.bus_info, vp->pdev->slot_name); - else - sprintf(info.bus_info, "EISA 0x%lx %d", - dev->base_addr, dev->irq); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - } - - return -EOPNOTSUPP; + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + if (vp->pdev) + strcpy(info->bus_info, pci_name(vp->pdev)); + else + sprintf(info->bus_info, "EISA 0x%lx %d", + dev->base_addr, dev->irq); } +static struct ethtool_ops vortex_ethtool_ops = { + .get_drvinfo = vortex_get_drvinfo, +}; + static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct vortex_private *vp = (struct vortex_private *)dev->priv; @@ -2728,9 +2719,6 @@ int retval; switch(cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - case SIOCGMIIPHY: /* Get address of MII PHY in use. */ case SIOCDEVPRIVATE: /* for binary compat, remove in 2.5 */ data->phy_id = phy; diff -Nru a/drivers/net/8139cp.c b/drivers/net/8139cp.c --- a/drivers/net/8139cp.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/8139cp.c Wed Aug 27 18:30:33 2003 @@ -30,8 +30,6 @@ * Constants (module parms?) for Rx work limit * Complete reset on PciErr * Consider Rx interrupt mitigation using TimerIntr - * Implement 8139C+ statistics dump; maybe not... - h/w stats can be reset only by software reset * Handle netif_rx return value * Investigate using skb->priority with h/w VLAN priority * Investigate using High Priority Tx Queue with skb->priority @@ -41,14 +39,13 @@ Tx descriptor bit * The real minimum of CP_MIN_MTU is 4 bytes. However, for this to be supported, one must(?) turn on packet padding. - * Support 8169 GMII * Support external MII transceivers */ #define DRV_NAME "8139cp" -#define DRV_VERSION "0.3.0" -#define DRV_RELDATE "Sep 29, 2002" +#define DRV_VERSION "0.5" +#define DRV_RELDATE "Aug 26, 2003" #include @@ -415,7 +412,7 @@ { "RTL-8169" }, }; -static struct pci_device_id cp_pci_tbl[] __devinitdata = { +static struct pci_device_id cp_pci_tbl[] = { { PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139Cp }, #if 0 @@ -658,7 +655,8 @@ cp->rx_tail = rx_tail; } -static void cp_interrupt (int irq, void *dev_instance, struct pt_regs *regs) +static irqreturn_t +cp_interrupt (int irq, void *dev_instance, struct pt_regs *regs) { struct net_device *dev = dev_instance; struct cp_private *cp = dev->priv; @@ -666,7 +664,7 @@ status = cpr16(IntrStatus); if (!status || (status == 0xFFFF)) - return; + return IRQ_NONE; if (netif_msg_intr(cp)) printk(KERN_DEBUG "%s: intr, status %04x cmd %02x cpcmd %04x\n", @@ -693,6 +691,7 @@ } spin_unlock(&cp->lock); + return IRQ_HANDLED; } static void cp_tx (struct cp_private *cp) @@ -826,7 +825,7 @@ * Otherwise we could race with the device. */ first_eor = eor; - first_len = skb->len - skb->data_len; + first_len = skb_headlen(skb); first_mapping = pci_map_single(cp->pdev, skb->data, first_len, PCI_DMA_TODEVICE); cp->tx_skb[entry].skb = skb; @@ -991,6 +990,8 @@ static void cp_stop_hw (struct cp_private *cp) { + struct net_device *dev = cp->dev; + cpw16(IntrMask, 0); cpr16(IntrMask); cpw8(Cmd, 0); @@ -1002,6 +1003,10 @@ cp->rx_tail = 0; cp->tx_head = cp->tx_tail = 0; + + (void) dev; /* avoid compiler warning when synchronize_irq() + * disappears during !CONFIG_SMP + */ } static void cp_reset_hw (struct cp_private *cp) @@ -1296,8 +1301,8 @@ } /* Set the ethtool Wake-on-LAN settings */ -static void netdev_set_wol (struct cp_private *cp, - const struct ethtool_wolinfo *wol) +static int netdev_set_wol (struct cp_private *cp, + const struct ethtool_wolinfo *wol) { u8 options; @@ -1324,6 +1329,8 @@ cpw8 (Config5, options); cp->wol_enabled = (wol->wolopts) ? 1 : 0; + + return 0; } /* Get the ethtool Wake-on-LAN settings */ @@ -1349,308 +1356,215 @@ if (options & MWF) wol->wolopts |= WAKE_MCAST; } -static int cp_ethtool_ioctl (struct cp_private *cp, void *useraddr) +static void cp_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info) { - u32 ethcmd; + struct cp_private *cp = dev->priv; - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ + strcpy (info->driver, DRV_NAME); + strcpy (info->version, DRV_VERSION); + strcpy (info->bus_info, pci_name(cp->pdev)); +} - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - strcpy (info.bus_info, cp->pdev->slot_name); - info.regdump_len = CP_REGS_SIZE; - info.n_stats = CP_NUM_STATS; - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } +static int cp_get_regs_len(struct net_device *dev) +{ + return CP_REGS_SIZE; +} - /* get settings */ - case ETHTOOL_GSET: { - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - spin_lock_irq(&cp->lock); - mii_ethtool_gset(&cp->mii_if, &ecmd); - spin_unlock_irq(&cp->lock); - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } - /* set settings */ - case ETHTOOL_SSET: { - int r; - struct ethtool_cmd ecmd; - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; - spin_lock_irq(&cp->lock); - r = mii_ethtool_sset(&cp->mii_if, &ecmd); - spin_unlock_irq(&cp->lock); - return r; - } - /* restart autonegotiation */ - case ETHTOOL_NWAY_RST: { - return mii_nway_restart(&cp->mii_if); - } - /* get link status */ - case ETHTOOL_GLINK: { - struct ethtool_value edata = {ETHTOOL_GLINK}; - edata.data = mii_link_ok(&cp->mii_if); - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } +static int cp_get_stats_count (struct net_device *dev) +{ + return CP_NUM_STATS; +} - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = cp->msg_enable; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - cp->msg_enable = edata.data; - return 0; - } +static int cp_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct cp_private *cp = dev->priv; + int rc; - /* NIC register dump */ - case ETHTOOL_GREGS: { - struct ethtool_regs regs; - u8 *regbuf = kmalloc(CP_REGS_SIZE, GFP_KERNEL); - int rc; - - if (!regbuf) - return -ENOMEM; - memset(regbuf, 0, CP_REGS_SIZE); + spin_lock_irq(&cp->lock); + rc = mii_ethtool_gset(&cp->mii_if, cmd); + spin_unlock_irq(&cp->lock); - rc = copy_from_user(®s, useraddr, sizeof(regs)); - if (rc) { - rc = -EFAULT; - goto err_out_gregs; - } - - if (regs.len > CP_REGS_SIZE) - regs.len = CP_REGS_SIZE; - if (regs.len < CP_REGS_SIZE) { - rc = -EINVAL; - goto err_out_gregs; - } + return rc; +} - regs.version = CP_REGS_VER; - rc = copy_to_user(useraddr, ®s, sizeof(regs)); - if (rc) { - rc = -EFAULT; - goto err_out_gregs; - } +static int cp_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct cp_private *cp = dev->priv; + int rc; - useraddr += offsetof(struct ethtool_regs, data); + spin_lock_irq(&cp->lock); + rc = mii_ethtool_sset(&cp->mii_if, cmd); + spin_unlock_irq(&cp->lock); - spin_lock_irq(&cp->lock); - memcpy_fromio(regbuf, cp->regs, CP_REGS_SIZE); - spin_unlock_irq(&cp->lock); + return rc; +} - if (copy_to_user(useraddr, regbuf, regs.len)) - rc = -EFAULT; +static int cp_nway_reset(struct net_device *dev) +{ + struct cp_private *cp = dev->priv; + return mii_nway_restart(&cp->mii_if); +} -err_out_gregs: - kfree(regbuf); - return rc; - } +static u32 cp_get_msglevel(struct net_device *dev) +{ + struct cp_private *cp = dev->priv; + return cp->msg_enable; +} - /* get/set RX checksumming */ - case ETHTOOL_GRXCSUM: { - struct ethtool_value edata = { ETHTOOL_GRXCSUM }; - u16 cmd = cpr16(CpCmd) & RxChkSum; - - edata.data = cmd ? 1 : 0; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - case ETHTOOL_SRXCSUM: { - struct ethtool_value edata; - u16 cmd = cpr16(CpCmd), newcmd; +static void cp_set_msglevel(struct net_device *dev, u32 value) +{ + struct cp_private *cp = dev->priv; + cp->msg_enable = value; +} - newcmd = cmd; +static u32 cp_get_rx_csum(struct net_device *dev) +{ + struct cp_private *cp = dev->priv; + return (cpr16(CpCmd) & RxChkSum) ? 1 : 0; +} - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; +static int cp_set_rx_csum(struct net_device *dev, u32 data) +{ + struct cp_private *cp = dev->priv; + u16 cmd = cpr16(CpCmd), newcmd; - if (edata.data) - newcmd |= RxChkSum; - else - newcmd &= ~RxChkSum; + newcmd = cmd; - if (newcmd == cmd) - return 0; + if (data) + newcmd |= RxChkSum; + else + newcmd &= ~RxChkSum; + if (newcmd != cmd) { spin_lock_irq(&cp->lock); cpw16_f(CpCmd, newcmd); spin_unlock_irq(&cp->lock); } - /* get/set TX checksumming */ - case ETHTOOL_GTXCSUM: { - struct ethtool_value edata = { ETHTOOL_GTXCSUM }; - - edata.data = (cp->dev->features & NETIF_F_IP_CSUM) != 0; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - case ETHTOOL_STXCSUM: { - struct ethtool_value edata; - - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; + return 0; +} - if (edata.data) - cp->dev->features |= NETIF_F_IP_CSUM; - else - cp->dev->features &= ~NETIF_F_IP_CSUM; +/* move this to net/core/ethtool.c */ +static int ethtool_op_set_tx_csum(struct net_device *dev, u32 data) +{ + if (data) + dev->features |= NETIF_F_IP_CSUM; + else + dev->features &= ~NETIF_F_IP_CSUM; - return 0; - } + return 0; +} - /* get/set scatter-gather */ - case ETHTOOL_GSG: { - struct ethtool_value edata = { ETHTOOL_GSG }; - - edata.data = (cp->dev->features & NETIF_F_SG) != 0; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - case ETHTOOL_SSG: { - struct ethtool_value edata; +static void cp_get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *p) +{ + struct cp_private *cp = dev->priv; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; + if (regs->len < CP_REGS_SIZE) + return /* -EINVAL */; - if (edata.data) - cp->dev->features |= NETIF_F_SG; - else - cp->dev->features &= ~NETIF_F_SG; + regs->version = CP_REGS_VER; - return 0; - } + spin_lock_irq(&cp->lock); + memcpy_fromio(p, cp->regs, CP_REGS_SIZE); + spin_unlock_irq(&cp->lock); +} - /* get string list(s) */ - case ETHTOOL_GSTRINGS: { - struct ethtool_gstrings estr = { ETHTOOL_GSTRINGS }; - - if (copy_from_user(&estr, useraddr, sizeof(estr))) - return -EFAULT; - if (estr.string_set != ETH_SS_STATS) - return -EINVAL; - - estr.len = CP_NUM_STATS; - if (copy_to_user(useraddr, &estr, sizeof(estr))) - return -EFAULT; - if (copy_to_user(useraddr + sizeof(estr), - ðtool_stats_keys, - sizeof(ethtool_stats_keys))) - return -EFAULT; - return 0; - } +static void cp_get_wol (struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct cp_private *cp = dev->priv; - /* get NIC-specific statistics */ - case ETHTOOL_GSTATS: { - struct ethtool_stats estats = { ETHTOOL_GSTATS }; - u64 *tmp_stats; - unsigned int work = 100; - const unsigned int sz = sizeof(u64) * CP_NUM_STATS; - int i; - - /* begin NIC statistics dump */ - cpw32(StatsAddr + 4, 0); /* FIXME: 64-bit PCI */ - cpw32(StatsAddr, cp->nic_stats_dma | DumpStats); - cpr32(StatsAddr); - - estats.n_stats = CP_NUM_STATS; - if (copy_to_user(useraddr, &estats, sizeof(estats))) - return -EFAULT; - - while (work-- > 0) { - if ((cpr32(StatsAddr) & DumpStats) == 0) - break; - cpu_relax(); - } + spin_lock_irq (&cp->lock); + netdev_get_wol (cp, wol); + spin_unlock_irq (&cp->lock); +} - if (cpr32(StatsAddr) & DumpStats) - return -EIO; +static int cp_set_wol (struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct cp_private *cp = dev->priv; + int rc; - tmp_stats = kmalloc(sz, GFP_KERNEL); - if (!tmp_stats) - return -ENOMEM; - memset(tmp_stats, 0, sz); - - i = 0; - tmp_stats[i++] = le64_to_cpu(cp->nic_stats->tx_ok); - tmp_stats[i++] = le64_to_cpu(cp->nic_stats->rx_ok); - tmp_stats[i++] = le64_to_cpu(cp->nic_stats->tx_err); - tmp_stats[i++] = le32_to_cpu(cp->nic_stats->rx_err); - tmp_stats[i++] = le16_to_cpu(cp->nic_stats->rx_fifo); - tmp_stats[i++] = le16_to_cpu(cp->nic_stats->frame_align); - tmp_stats[i++] = le32_to_cpu(cp->nic_stats->tx_ok_1col); - tmp_stats[i++] = le32_to_cpu(cp->nic_stats->tx_ok_mcol); - tmp_stats[i++] = le64_to_cpu(cp->nic_stats->rx_ok_phys); - tmp_stats[i++] = le64_to_cpu(cp->nic_stats->rx_ok_bcast); - tmp_stats[i++] = le32_to_cpu(cp->nic_stats->rx_ok_mcast); - tmp_stats[i++] = le16_to_cpu(cp->nic_stats->tx_abort); - tmp_stats[i++] = le16_to_cpu(cp->nic_stats->tx_underrun); - tmp_stats[i++] = cp->cp_stats.rx_frags; - if (i != CP_NUM_STATS) - BUG(); + spin_lock_irq (&cp->lock); + rc = netdev_set_wol (cp, wol); + spin_unlock_irq (&cp->lock); - i = copy_to_user(useraddr + sizeof(estats), - tmp_stats, sz); - kfree(tmp_stats); + return rc; +} - if (i) - return -EFAULT; - return 0; +static void cp_get_strings (struct net_device *dev, u32 stringset, u8 *buf) +{ + switch (stringset) { + case ETH_SS_STATS: + memcpy(buf, ðtool_stats_keys, sizeof(ethtool_stats_keys)); + break; + default: + BUG(); + break; } +} - /* get/set Wake-on-LAN settings */ - case ETHTOOL_GWOL: { - struct ethtool_wolinfo wol = { ETHTOOL_GWOL }; - - spin_lock_irq (&cp->lock); - netdev_get_wol (cp, &wol); - spin_unlock_irq (&cp->lock); - return ((copy_to_user (useraddr, &wol, sizeof (wol)))? -EFAULT : 0); - } - - case ETHTOOL_SWOL: { - struct ethtool_wolinfo wol; +static void cp_get_ethtool_stats (struct net_device *dev, + struct ethtool_stats *estats, u64 *tmp_stats) +{ + struct cp_private *cp = dev->priv; + unsigned int work = 100; + int i; - if (copy_from_user (&wol, useraddr, sizeof (wol))) - return -EFAULT; - spin_lock_irq (&cp->lock); - netdev_set_wol (cp, &wol); - spin_unlock_irq (&cp->lock); - return 0; - } + /* begin NIC statistics dump */ + cpw32(StatsAddr + 4, 0); /* FIXME: 64-bit PCI */ + cpw32(StatsAddr, cp->nic_stats_dma | DumpStats); + cpr32(StatsAddr); - default: - break; + while (work-- > 0) { + if ((cpr32(StatsAddr) & DumpStats) == 0) + break; + cpu_relax(); } - return -EOPNOTSUPP; -} + if (cpr32(StatsAddr) & DumpStats) + return /* -EIO */; + i = 0; + tmp_stats[i++] = le64_to_cpu(cp->nic_stats->tx_ok); + tmp_stats[i++] = le64_to_cpu(cp->nic_stats->rx_ok); + tmp_stats[i++] = le64_to_cpu(cp->nic_stats->tx_err); + tmp_stats[i++] = le32_to_cpu(cp->nic_stats->rx_err); + tmp_stats[i++] = le16_to_cpu(cp->nic_stats->rx_fifo); + tmp_stats[i++] = le16_to_cpu(cp->nic_stats->frame_align); + tmp_stats[i++] = le32_to_cpu(cp->nic_stats->tx_ok_1col); + tmp_stats[i++] = le32_to_cpu(cp->nic_stats->tx_ok_mcol); + tmp_stats[i++] = le64_to_cpu(cp->nic_stats->rx_ok_phys); + tmp_stats[i++] = le64_to_cpu(cp->nic_stats->rx_ok_bcast); + tmp_stats[i++] = le32_to_cpu(cp->nic_stats->rx_ok_mcast); + tmp_stats[i++] = le16_to_cpu(cp->nic_stats->tx_abort); + tmp_stats[i++] = le16_to_cpu(cp->nic_stats->tx_underrun); + tmp_stats[i++] = cp->cp_stats.rx_frags; + if (i != CP_NUM_STATS) + BUG(); +} + +static struct ethtool_ops cp_ethtool_ops = { + .get_drvinfo = cp_get_drvinfo, + .get_regs_len = cp_get_regs_len, + .get_stats_count = cp_get_stats_count, + .get_settings = cp_get_settings, + .set_settings = cp_set_settings, + .nway_reset = cp_nway_reset, + .get_link = ethtool_op_get_link, + .get_msglevel = cp_get_msglevel, + .set_msglevel = cp_set_msglevel, + .get_rx_csum = cp_get_rx_csum, + .set_rx_csum = cp_set_rx_csum, + .get_tx_csum = ethtool_op_get_tx_csum, + .set_tx_csum = ethtool_op_set_tx_csum, /* local! */ + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, + .get_regs = cp_get_regs, + .get_wol = cp_get_wol, + .set_wol = cp_set_wol, + .get_strings = cp_get_strings, + .get_ethtool_stats = cp_get_ethtool_stats, +}; static int cp_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) { @@ -1661,9 +1575,6 @@ if (!netif_running(dev)) return -EINVAL; - if (cmd == SIOCETHTOOL) - return cp_ethtool_ioctl(cp, (void *) rq->ifr_data); - spin_lock_irq(&cp->lock); rc = generic_mii_ioctl(&cp->mii_if, mii, cmd, NULL); spin_unlock_irq(&cp->lock); @@ -1784,7 +1695,7 @@ if (pdev->vendor == PCI_VENDOR_ID_REALTEK && pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev < 0x20) { printk(KERN_ERR PFX "pci dev %s (id %04x:%04x rev %02x) is not an 8139C+ compatible chip\n", - pdev->slot_name, pdev->vendor, pdev->device, pci_rev); + pci_name(pdev), pdev->vendor, pdev->device, pci_rev); printk(KERN_ERR PFX "Try the \"8139too\" driver instead.\n"); return -ENODEV; } @@ -1818,20 +1729,20 @@ if (pdev->irq < 2) { rc = -EIO; printk(KERN_ERR PFX "invalid irq (%d) for pci dev %s\n", - pdev->irq, pdev->slot_name); + pdev->irq, pci_name(pdev)); goto err_out_res; } pciaddr = pci_resource_start(pdev, 1); if (!pciaddr) { rc = -EIO; printk(KERN_ERR PFX "no MMIO resource for pci dev %s\n", - pdev->slot_name); + pci_name(pdev)); goto err_out_res; } if (pci_resource_len(pdev, 1) < CP_REGS_SIZE) { rc = -EIO; printk(KERN_ERR PFX "MMIO resource (%lx) too small on pci dev %s\n", - pci_resource_len(pdev, 1), pdev->slot_name); + pci_resource_len(pdev, 1), pci_name(pdev)); goto err_out_res; } @@ -1852,7 +1763,7 @@ if (!regs) { rc = -EIO; printk(KERN_ERR PFX "Cannot map PCI MMIO (%lx@%lx) on pci dev %s\n", - pci_resource_len(pdev, 1), pciaddr, pdev->slot_name); + pci_resource_len(pdev, 1), pciaddr, pci_name(pdev)); goto err_out_res; } dev->base_addr = (unsigned long) regs; @@ -1875,6 +1786,7 @@ #ifdef BROKEN dev->change_mtu = cp_change_mtu; #endif + dev->ethtool_ops = &cp_ethtool_ops; #if 0 dev->tx_timeout = cp_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; diff -Nru a/drivers/net/8139too.c b/drivers/net/8139too.c --- a/drivers/net/8139too.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/8139too.c Wed Aug 27 18:30:33 2003 @@ -247,7 +247,7 @@ }; -static struct pci_device_id rtl8139_pci_tbl[] __devinitdata = { +static struct pci_device_id rtl8139_pci_tbl[] = { {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 }, {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139_CB }, {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SMC1211TX }, @@ -301,7 +301,6 @@ IntrMask = 0x3C, IntrStatus = 0x3E, TxConfig = 0x40, - ChipVersion = 0x43, RxConfig = 0x44, Timer = 0x48, /* A general-purpose counter. */ RxMissed = 0x4C, /* 24 bits valid, write clears. */ @@ -498,9 +497,13 @@ CH_8139 = 0, CH_8139_K, CH_8139A, + CH_8139A_G, CH_8139B, CH_8130, CH_8139C, + CH_8100, + CH_8100B_8139D, + CH_8101, } chip_t; enum chip_flags { @@ -508,50 +511,65 @@ HasLWake = (1 << 1), }; +#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \ + (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22) +#define HW_REVID_MASK HW_REVID(1, 1, 1, 1, 1, 1, 1) /* directly indexed by chip_t, above */ const static struct { const char *name; - u8 version; /* from RTL8139C docs */ - u32 RxConfigMask; /* should clear the bits supported by this chip */ + u32 version; /* from RTL8139C/RTL8139D docs */ u32 flags; } rtl_chip_info[] = { { "RTL-8139", - 0x40, - 0xf0fe0040, /* XXX copied from RTL8139A, verify */ + HW_REVID(1, 0, 0, 0, 0, 0, 0), HasHltClk, }, { "RTL-8139 rev K", - 0x60, - 0xf0fe0040, + HW_REVID(1, 1, 0, 0, 0, 0, 0), HasHltClk, }, { "RTL-8139A", - 0x70, - 0xf0fe0040, + HW_REVID(1, 1, 1, 0, 0, 0, 0), + HasHltClk, /* XXX undocumented? */ + }, + + { "RTL-8139A rev G", + HW_REVID(1, 1, 1, 0, 0, 1, 0), HasHltClk, /* XXX undocumented? */ }, { "RTL-8139B", - 0x78, - 0xf0fc0040, + HW_REVID(1, 1, 1, 1, 0, 0, 0), HasLWake, }, { "RTL-8130", - 0x7C, - 0xf0fe0040, /* XXX copied from RTL8139A, verify */ + HW_REVID(1, 1, 1, 1, 1, 0, 0), HasLWake, }, { "RTL-8139C", - 0x74, - 0xf0fc0040, /* XXX copied from RTL8139B, verify */ + HW_REVID(1, 1, 1, 0, 1, 0, 0), HasLWake, }, + { "RTL-8100", + HW_REVID(1, 1, 1, 1, 0, 1, 0), + HasLWake, + }, + + { "RTL-8100B/8139D", + HW_REVID(1, 1, 1, 0, 1, 0, 1), + HasLWake, + }, + + { "RTL-8101", + HW_REVID(1, 1, 1, 0, 1, 1, 1), + HasLWake, + }, }; struct rtl_extra_stats { @@ -616,7 +634,7 @@ static void rtl8139_init_ring (struct net_device *dev); static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev); -static void rtl8139_interrupt (int irq, void *dev_instance, +static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance, struct pt_regs *regs); static int rtl8139_close (struct net_device *dev); static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); @@ -624,6 +642,7 @@ static void rtl8139_set_rx_mode (struct net_device *dev); static void __set_rx_mode (struct net_device *dev); static void rtl8139_hw_start (struct net_device *dev); +static struct ethtool_ops rtl8139_ethtool_ops; #ifdef USE_IO_OPS @@ -756,7 +775,7 @@ unsigned int i; u32 pio_start, pio_end, pio_flags, pio_len; unsigned long mmio_start, mmio_end, mmio_flags, mmio_len; - u32 tmp; + u32 version; assert (pdev != NULL); @@ -765,10 +784,11 @@ /* dev and dev->priv zeroed in alloc_etherdev */ dev = alloc_etherdev (sizeof (*tp)); if (dev == NULL) { - printk (KERN_ERR PFX "%s: Unable to alloc new net device\n", pdev->slot_name); + printk (KERN_ERR PFX "%s: Unable to alloc new net device\n", pci_name(pdev)); return -ENOMEM; } SET_MODULE_OWNER(dev); + tp = dev->priv; tp->pci_dev = pdev; @@ -795,25 +815,25 @@ #ifdef USE_IO_OPS /* make sure PCI base addr 0 is PIO */ if (!(pio_flags & IORESOURCE_IO)) { - printk (KERN_ERR PFX "%s: region #0 not a PIO resource, aborting\n", pdev->slot_name); + printk (KERN_ERR PFX "%s: region #0 not a PIO resource, aborting\n", pci_name(pdev)); rc = -ENODEV; goto err_out; } /* check for weird/broken PCI region reporting */ if (pio_len < RTL_MIN_IO_SIZE) { - printk (KERN_ERR PFX "%s: Invalid PCI I/O region size(s), aborting\n", pdev->slot_name); + printk (KERN_ERR PFX "%s: Invalid PCI I/O region size(s), aborting\n", pci_name(pdev)); rc = -ENODEV; goto err_out; } #else /* make sure PCI base addr 1 is MMIO */ if (!(mmio_flags & IORESOURCE_MEM)) { - printk (KERN_ERR PFX "%s: region #1 not an MMIO resource, aborting\n", pdev->slot_name); + printk (KERN_ERR PFX "%s: region #1 not an MMIO resource, aborting\n", pci_name(pdev)); rc = -ENODEV; goto err_out; } if (mmio_len < RTL_MIN_IO_SIZE) { - printk (KERN_ERR PFX "%s: Invalid PCI mem region size(s), aborting\n", pdev->slot_name); + printk (KERN_ERR PFX "%s: Invalid PCI mem region size(s), aborting\n", pci_name(pdev)); rc = -ENODEV; goto err_out; } @@ -835,7 +855,7 @@ /* ioremap MMIO region */ ioaddr = ioremap (mmio_start, mmio_len); if (ioaddr == NULL) { - printk (KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", pdev->slot_name); + printk (KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", pci_name(pdev)); rc = -EIO; goto err_out; } @@ -850,23 +870,23 @@ /* check for missing/broken hardware */ if (RTL_R32 (TxConfig) == 0xFFFFFFFF) { printk (KERN_ERR PFX "%s: Chip not responding, ignoring board\n", - pdev->slot_name); + pci_name(pdev)); rc = -EIO; goto err_out; } /* identify chip attached to board */ - tmp = RTL_R8 (ChipVersion); + version = RTL_R32 (TxConfig) & HW_REVID_MASK; for (i = 0; i < ARRAY_SIZE (rtl_chip_info); i++) - if (tmp == rtl_chip_info[i].version) { + if (version == rtl_chip_info[i].version) { tp->chipset = i; goto match; } /* if unknown chip, assume array element #0, original RTL-8139 in this case */ printk (KERN_DEBUG PFX "%s: unknown chip version, assuming RTL-8139\n", - pdev->slot_name); - printk (KERN_DEBUG PFX "%s: TxConfig = 0x%lx\n", pdev->slot_name, RTL_R32 (TxConfig)); + pci_name(pdev)); + printk (KERN_DEBUG PFX "%s: TxConfig = 0x%lx\n", pci_name(pdev), RTL_R32 (TxConfig)); tp->chipset = 0; match: @@ -889,8 +909,11 @@ } if (rtl_chip_info[tp->chipset].flags & HasLWake) { tmp8 = RTL_R8 (Config4); - if (tmp8 & LWPTN) + if (tmp8 & LWPTN) { + RTL_W8 (Cfg9346, Cfg9346_Unlock); RTL_W8 (Config4, tmp8 & ~LWPTN); + RTL_W8 (Cfg9346, Cfg9346_Lock); + } } } else { DPRINTK("Old chip wakeup\n"); @@ -941,7 +964,7 @@ if (pdev->vendor == PCI_VENDOR_ID_REALTEK && pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev >= 0x20) { printk(KERN_INFO PFX "pci dev %s (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n", - pdev->slot_name, pdev->vendor, pdev->device, pci_rev); + pci_name(pdev), pdev->vendor, pdev->device, pci_rev); printk(KERN_INFO PFX "Use the \"8139cp\" driver for improved performance and stability.\n"); } @@ -968,6 +991,7 @@ dev->get_stats = rtl8139_get_stats; dev->set_multicast_list = rtl8139_set_rx_mode; dev->do_ioctl = netdev_ioctl; + dev->ethtool_ops = &rtl8139_ethtool_ops; dev->tx_timeout = rtl8139_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; @@ -1336,23 +1360,12 @@ } -static void rtl_check_media (struct net_device *dev) +static void rtl_check_media (struct net_device *dev, unsigned int init_media) { struct rtl8139_private *tp = dev->priv; if (tp->phys[0] >= 0) { - u16 mii_lpa = mdio_read(dev, tp->phys[0], MII_LPA); - if (mii_lpa == 0xffff) - ; /* Not there */ - else if ((mii_lpa & LPA_100FULL) == LPA_100FULL - || (mii_lpa & 0x00C0) == LPA_10FULL) - tp->mii.full_duplex = 1; - - printk (KERN_INFO"%s: Setting %s%s-duplex based on" - " auto-negotiated partner ability %4.4x.\n", - dev->name, mii_lpa == 0 ? "" : - (mii_lpa & 0x0180) ? "100mbps " : "10mbps ", - tp->mii.full_duplex ? "full" : "half", mii_lpa); + mii_check_media(&tp->mii, 1, init_media); } } @@ -1387,7 +1400,7 @@ tp->cur_rx = 0; - rtl_check_media (dev); + rtl_check_media (dev, 1); if (tp->chipset >= CH_8139B) { /* Disable magic packet scanning, which is enabled @@ -2002,18 +2015,7 @@ if ((status & RxUnderrun) && link_changed && (tp->drv_flags & HAS_LNK_CHNG)) { - /* Really link-change on new chips. */ - int lpar = RTL_R16 (NWayLPAR); - int duplex = (lpar & LPA_100FULL) || (lpar & 0x01C0) == 0x0040 - || tp->mii.force_media; - if (tp->mii.full_duplex != duplex) { - tp->mii.full_duplex = duplex; -#if 0 - RTL_W8 (Cfg9346, Cfg9346_Unlock); - RTL_W8 (Config1, tp->mii.full_duplex ? 0x60 : 0x20); - RTL_W8 (Cfg9346, Cfg9346_Lock); -#endif - } + rtl_check_media(dev, 0); status &= ~RxUnderrun; } @@ -2039,7 +2041,7 @@ /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ -static void rtl8139_interrupt (int irq, void *dev_instance, +static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance, struct pt_regs *regs) { struct net_device *dev = (struct net_device *) dev_instance; @@ -2048,6 +2050,7 @@ void *ioaddr = tp->mmio_addr; int ackstat, status; int link_changed = 0; /* avoid bogus "uninit" warning */ + int handled = 0; spin_lock (&tp->lock); @@ -2063,6 +2066,8 @@ RxFIFOOver | TxErr | TxOK | RxErr | RxOK)) == 0) break; + handled = 1; + /* Acknowledge all of the current interrupt sources ASAP, but an first get an additional status bit from CSCR. */ if (status & RxUnderrun) @@ -2107,6 +2112,7 @@ DPRINTK ("%s: exiting interrupt, intr_status=%#4.4x.\n", dev->name, RTL_R16 (IntrStatus)); + return IRQ_RETVAL(handled); } @@ -2172,11 +2178,12 @@ /* Get the ethtool Wake-on-LAN settings. Assumes that wol points to kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and other threads or interrupts aren't messing with the 8139. */ -static void netdev_get_wol (struct net_device *dev, struct ethtool_wolinfo *wol) +static void rtl8139_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct rtl8139_private *np = dev->priv; void *ioaddr = np->mmio_addr; + spin_lock_irq(&np->lock); if (rtl_chip_info[np->chipset].flags & HasLWake) { u8 cfg3 = RTL_R8 (Config3); u8 cfg5 = RTL_R8 (Config5); @@ -2198,14 +2205,14 @@ if (cfg5 & Cfg5_BWF) wol->wolopts |= WAKE_BCAST; } + spin_unlock_irq(&np->lock); } /* Set the ethtool Wake-on-LAN settings. Return 0 or -errno. Assumes that wol points to kernel memory and other threads or interrupts aren't messing with the 8139. */ -static int netdev_set_wol (struct net_device *dev, - const struct ethtool_wolinfo *wol) +static int rtl8139_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct rtl8139_private *np = dev->priv; void *ioaddr = np->mmio_addr; @@ -2219,6 +2226,7 @@ if (wol->wolopts & ~support) return -EINVAL; + spin_lock_irq(&np->lock); cfg3 = RTL_R8 (Config3) & ~(Cfg3_LinkUp | Cfg3_Magic); if (wol->wolopts & WAKE_PHY) cfg3 |= Cfg3_LinkUp; @@ -2239,213 +2247,120 @@ if (wol->wolopts & WAKE_BCAST) cfg5 |= Cfg5_BWF; RTL_W8 (Config5, cfg5); /* need not unlock via Cfg9346 */ + spin_unlock_irq(&np->lock); return 0; } -static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) +static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct rtl8139_private *np = dev->priv; - u32 ethcmd; - - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, pci_name(np->pci_dev)); + info->regdump_len = np->regs_len; +} - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; +static int rtl8139_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct rtl8139_private *np = dev->priv; + spin_lock_irq(&np->lock); + mii_ethtool_gset(&np->mii, cmd); + spin_unlock_irq(&np->lock); + return 0; +} - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - strcpy (info.bus_info, np->pci_dev->slot_name); - info.regdump_len = np->regs_len; - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } +static int rtl8139_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct rtl8139_private *np = dev->priv; + int rc; + spin_lock_irq(&np->lock); + rc = mii_ethtool_sset(&np->mii, cmd); + spin_unlock_irq(&np->lock); + return rc; +} - /* get settings */ - case ETHTOOL_GSET: { - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - spin_lock_irq(&np->lock); - mii_ethtool_gset(&np->mii, &ecmd); - spin_unlock_irq(&np->lock); - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } - /* set settings */ - case ETHTOOL_SSET: { - int r; - struct ethtool_cmd ecmd; - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; - spin_lock_irq(&np->lock); - r = mii_ethtool_sset(&np->mii, &ecmd); - spin_unlock_irq(&np->lock); - return r; - } - /* restart autonegotiation */ - case ETHTOOL_NWAY_RST: { - return mii_nway_restart(&np->mii); - } - /* get link status */ - case ETHTOOL_GLINK: { - struct ethtool_value edata = {ETHTOOL_GLINK}; - edata.data = mii_link_ok(&np->mii); - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } +static int rtl8139_nway_reset(struct net_device *dev) +{ + struct rtl8139_private *np = dev->priv; + return mii_nway_restart(&np->mii); +} - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = debug; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - debug = edata.data; - return 0; - } +static u32 rtl8139_get_link(struct net_device *dev) +{ + struct rtl8139_private *np = dev->priv; + return mii_link_ok(&np->mii); +} - case ETHTOOL_GWOL: - { - struct ethtool_wolinfo wol = { ETHTOOL_GWOL }; - spin_lock_irq (&np->lock); - netdev_get_wol (dev, &wol); - spin_unlock_irq (&np->lock); - if (copy_to_user (useraddr, &wol, sizeof (wol))) - return -EFAULT; - return 0; - } +static u32 rtl8139_get_msglevel(struct net_device *dev) +{ + return debug; +} - case ETHTOOL_SWOL: - { - struct ethtool_wolinfo wol; - int rc; - if (copy_from_user (&wol, useraddr, sizeof (wol))) - return -EFAULT; - spin_lock_irq (&np->lock); - rc = netdev_set_wol (dev, &wol); - spin_unlock_irq (&np->lock); - return rc; - } +static void rtl8139_set_msglevel(struct net_device *dev, u32 datum) +{ + debug = datum; +} /* TODO: we are too slack to do reg dumping for pio, for now */ -#ifndef CONFIG_8139TOO_PIO - /* NIC register dump */ - case ETHTOOL_GREGS: { - struct ethtool_regs regs; - unsigned int regs_len = np->regs_len; - u8 *regbuf = kmalloc(regs_len, GFP_KERNEL); - int rc; - - if (!regbuf) - return -ENOMEM; - memset(regbuf, 0, regs_len); - - rc = copy_from_user(®s, useraddr, sizeof(regs)); - if (rc) { - rc = -EFAULT; - goto err_out_gregs; - } - - if (regs.len > regs_len) - regs.len = regs_len; - if (regs.len < regs_len) { - rc = -EINVAL; - goto err_out_gregs; - } - - regs.version = RTL_REGS_VER; - rc = copy_to_user(useraddr, ®s, sizeof(regs)); - if (rc) { - rc = -EFAULT; - goto err_out_gregs; - } - - useraddr += offsetof(struct ethtool_regs, data); - - spin_lock_irq(&np->lock); - memcpy_fromio(regbuf, np->mmio_addr, regs_len); - spin_unlock_irq(&np->lock); - - if (copy_to_user(useraddr, regbuf, regs_len)) - rc = -EFAULT; - -err_out_gregs: - kfree(regbuf); - return rc; - } -#endif /* CONFIG_8139TOO_PIO */ - - /* get string list(s) */ - case ETHTOOL_GSTRINGS: { - struct ethtool_gstrings estr = { ETHTOOL_GSTRINGS }; - - if (copy_from_user(&estr, useraddr, sizeof(estr))) - return -EFAULT; - if (estr.string_set != ETH_SS_STATS) - return -EINVAL; - - estr.len = RTL_NUM_STATS; - if (copy_to_user(useraddr, &estr, sizeof(estr))) - return -EFAULT; - if (copy_to_user(useraddr + sizeof(estr), - ðtool_stats_keys, - sizeof(ethtool_stats_keys))) - return -EFAULT; - return 0; - } +#ifdef CONFIG_8139TOO_PIO +#define rtl8139_get_regs_len NULL +#define rtl8139_get_regs NULL +#else +static int rtl8139_get_regs_len(struct net_device *dev) +{ + struct rtl8139_private *np = dev->priv; + return np->regs_len; +} - /* get NIC-specific statistics */ - case ETHTOOL_GSTATS: { - struct ethtool_stats estats = { ETHTOOL_GSTATS }; - u64 *tmp_stats; - const unsigned int sz = sizeof(u64) * RTL_NUM_STATS; - int i; - - estats.n_stats = RTL_NUM_STATS; - if (copy_to_user(useraddr, &estats, sizeof(estats))) - return -EFAULT; - - tmp_stats = kmalloc(sz, GFP_KERNEL); - if (!tmp_stats) - return -ENOMEM; - memset(tmp_stats, 0, sz); - - i = 0; - tmp_stats[i++] = np->xstats.early_rx; - tmp_stats[i++] = np->xstats.tx_buf_mapped; - tmp_stats[i++] = np->xstats.tx_timeouts; - tmp_stats[i++] = np->xstats.rx_lost_in_ring; - if (i != RTL_NUM_STATS) - BUG(); +static void rtl8139_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf) +{ + struct rtl8139_private *np = dev->priv; - i = copy_to_user(useraddr + sizeof(estats), tmp_stats, sz); - kfree(tmp_stats); + regs->version = RTL_REGS_VER; - if (i) - return -EFAULT; - return 0; - } - default: - break; - } + spin_lock_irq(&np->lock); + memcpy_fromio(regbuf, np->mmio_addr, regs->len); + spin_unlock_irq(&np->lock); +} +#endif /* CONFIG_8139TOO_MMIO */ - return -EOPNOTSUPP; +static int rtl8139_get_stats_count(struct net_device *dev) +{ + return RTL_NUM_STATS; } +static void rtl8139_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) +{ + struct rtl8139_private *np = dev->priv; + + data[0] = np->xstats.early_rx; + data[1] = np->xstats.tx_buf_mapped; + data[2] = np->xstats.tx_timeouts; + data[3] = np->xstats.rx_lost_in_ring; +} + +static void rtl8139_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys)); +} + +static struct ethtool_ops rtl8139_ethtool_ops = { + .get_drvinfo = rtl8139_get_drvinfo, + .get_settings = rtl8139_get_settings, + .set_settings = rtl8139_set_settings, + .get_regs_len = rtl8139_get_regs_len, + .get_regs = rtl8139_get_regs, + .nway_reset = rtl8139_nway_reset, + .get_link = rtl8139_get_link, + .get_msglevel = rtl8139_get_msglevel, + .set_msglevel = rtl8139_set_msglevel, + .get_wol = rtl8139_get_wol, + .set_wol = rtl8139_set_wol, + .get_strings = rtl8139_get_strings, + .get_stats_count = rtl8139_get_stats_count, + .get_ethtool_stats = rtl8139_get_ethtool_stats, +}; static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { @@ -2456,14 +2371,9 @@ if (!netif_running(dev)) return -EINVAL; - if (cmd == SIOCETHTOOL) - rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - - else { - spin_lock_irq(&np->lock); - rc = generic_mii_ioctl(&np->mii, data, cmd, NULL); - spin_unlock_irq(&np->lock); - } + spin_lock_irq(&np->lock); + rc = generic_mii_ioctl(&np->mii, data, cmd, NULL); + spin_unlock_irq(&np->lock); return rc; } diff -Nru a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c --- a/drivers/net/bonding/bond_alb.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/bonding/bond_alb.c Wed Aug 27 18:30:33 2003 @@ -24,10 +24,15 @@ * 2003/06/25 - Shmulik Hen * - Fixed signed/unsigned calculation errors that caused load sharing * to collapse to one slave under very heavy UDP Tx stress. + * + * 2003/08/06 - Amir Noam + * - Add support for setting bond's MAC address with special + * handling required for ALB/TLB. */ #include #include +#include #include #include #include @@ -943,10 +948,11 @@ } /* hw is a boolean parameter that determines whether we should try and - * set the hw address of the hw as well as the hw address of the net_device + * set the hw address of the device as well as the hw address of the + * net_device */ static int -alb_set_mac_addr(struct slave *slave, u8 addr[], int hw) +alb_set_slave_mac_addr(struct slave *slave, u8 addr[], int hw) { struct net_device *dev = NULL; struct sockaddr s_addr; @@ -954,16 +960,16 @@ dev = slave->dev; if (!hw) { - memcpy(dev->dev_addr, addr, ETH_ALEN); + memcpy(dev->dev_addr, addr, dev->addr_len); return 0; } /* for rlb each slave must have a unique hw mac addresses so that */ /* each slave will receive packets destined to a different mac */ - memcpy(s_addr.sa_data, addr, ETH_ALEN); + memcpy(s_addr.sa_data, addr, dev->addr_len); s_addr.sa_family = dev->type; if (dev->set_mac_address(dev, &s_addr)) { - printk(KERN_DEBUG "bonding: Error: alb_set_mac_addr:" + printk(KERN_DEBUG "bonding: Error: alb_set_slave_mac_addr:" " dev->set_mac_address of dev %s failed!" " ALB mode requires that the base driver" " support setting the hw address also when" @@ -987,8 +993,8 @@ slaves_state_differ = (SLAVE_IS_OK(slave1) != SLAVE_IS_OK(slave2)); memcpy(tmp_mac_addr, slave1->dev->dev_addr, ETH_ALEN); - alb_set_mac_addr(slave1, slave2->dev->dev_addr, bond->alb_info.rlb_enabled); - alb_set_mac_addr(slave2, tmp_mac_addr, bond->alb_info.rlb_enabled); + alb_set_slave_mac_addr(slave1, slave2->dev->dev_addr, bond->alb_info.rlb_enabled); + alb_set_slave_mac_addr(slave2, tmp_mac_addr, bond->alb_info.rlb_enabled); /* fasten the change in the switch */ if (SLAVE_IS_OK(slave1)) { @@ -1153,8 +1159,8 @@ } if (tmp_slave1) { - alb_set_mac_addr(slave, tmp_slave1->perm_hwaddr, - bond->alb_info.rlb_enabled); + alb_set_slave_mac_addr(slave, tmp_slave1->perm_hwaddr, + bond->alb_info.rlb_enabled); printk(KERN_WARNING "bonding: Warning: the hw address " "of slave %s is in use by the bond; " @@ -1172,6 +1178,67 @@ return 0; } +/** + * alb_set_mac_address + * @bond: + * @addr: + * + * In TLB mode all slaves are configured to the bond's hw address, but set + * their dev_addr field to different addresses (based on their permanent hw + * addresses). + * + * For each slave, this function sets the interface to the new address and then + * changes its dev_addr field to its previous value. + * + * Unwinding assumes bond's mac address has not yet changed. + */ +static inline int +alb_set_mac_address(struct bonding *bond, void *addr) +{ + struct sockaddr sa; + struct slave *slave; + char tmp_addr[ETH_ALEN]; + int error; + + if (bond->alb_info.rlb_enabled) { + return 0; + } + + slave = bond_get_first_slave(bond); + for (; slave; slave = bond_get_next_slave(bond, slave)) { + if (slave->dev->set_mac_address == NULL) { + error = -EOPNOTSUPP; + goto unwind; + } + + /* save net_device's current hw address */ + memcpy(tmp_addr, slave->dev->dev_addr, ETH_ALEN); + + error = slave->dev->set_mac_address(slave->dev, addr); + + /* restore net_device's hw address */ + memcpy(slave->dev->dev_addr, tmp_addr, ETH_ALEN); + + if (error) { + goto unwind; + } + } + + return 0; + +unwind: + memcpy(sa.sa_data, bond->device->dev_addr, bond->device->addr_len); + sa.sa_family = bond->device->type; + slave = bond_get_first_slave(bond); + for (; slave; slave = bond_get_next_slave(bond, slave)) { + memcpy(tmp_addr, slave->dev->dev_addr, ETH_ALEN); + slave->dev->set_mac_address(slave->dev, &sa); + memcpy(slave->dev->dev_addr, tmp_addr, ETH_ALEN); + } + + return error; +} + /************************ exported alb funcions ************************/ int @@ -1442,8 +1509,8 @@ { int err = 0; - err = alb_set_mac_addr(slave, slave->perm_hwaddr, - bond->alb_info.rlb_enabled); + err = alb_set_slave_mac_addr(slave, slave->perm_hwaddr, + bond->alb_info.rlb_enabled); if (err) { return err; } @@ -1567,10 +1634,61 @@ alb_swap_mac_addr(bond, swap_slave, new_slave); } else { /* set the new_slave to the bond mac address */ - alb_set_mac_addr(new_slave, bond->device->dev_addr, - bond->alb_info.rlb_enabled); + alb_set_slave_mac_addr(new_slave, bond->device->dev_addr, + bond->alb_info.rlb_enabled); /* fasten bond mac on new current slave */ alb_send_learning_packets(new_slave, bond->device->dev_addr); } +} + +int +bond_alb_set_mac_address(struct net_device *dev, void *addr) +{ + struct bonding *bond = dev->priv; + struct sockaddr *sa = addr; + struct slave *swap_slave = NULL; + int error = 0; + + if (!is_valid_ether_addr(sa->sa_data)) { + return -EADDRNOTAVAIL; + } + + error = alb_set_mac_address(bond, addr); + if (error) { + return error; + } + + memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); + + /* If there is no current_slave there is nothing else to do. + * Otherwise we'll need to pass the new address to it and handle + * duplications. + */ + if (bond->current_slave == NULL) { + return 0; + } + + swap_slave = bond_get_first_slave(bond); + while (swap_slave) { + if (!memcmp(swap_slave->dev->dev_addr, dev->dev_addr, ETH_ALEN)) { + break; + } + swap_slave = bond_get_next_slave(bond, swap_slave); + } + + if (swap_slave) { + alb_swap_mac_addr(bond, swap_slave, bond->current_slave); + } else { + alb_set_slave_mac_addr(bond->current_slave, dev->dev_addr, + bond->alb_info.rlb_enabled); + + alb_send_learning_packets(bond->current_slave, dev->dev_addr); + if (bond->alb_info.rlb_enabled) { + /* inform clients mac address has changed */ + rlb_req_update_slave_clients(bond, bond->current_slave); + } + } + + return 0; } diff -Nru a/drivers/net/bonding/bond_alb.h b/drivers/net/bonding/bond_alb.h --- a/drivers/net/bonding/bond_alb.h Wed Aug 27 18:30:33 2003 +++ b/drivers/net/bonding/bond_alb.h Wed Aug 27 18:30:33 2003 @@ -17,6 +17,13 @@ * * The full GNU General Public License is included in this distribution in the * file called LICENSE. + * + * + * Changes: + * + * 2003/08/06 - Amir Noam + * - Add support for setting bond's MAC address with special + * handling required for ALB/TLB. */ #ifndef __BOND_ALB_H__ @@ -122,6 +129,7 @@ void bond_alb_assign_current_slave(struct bonding *bond, struct slave *new_slave); int bond_alb_xmit(struct sk_buff *skb, struct net_device *dev); void bond_alb_monitor(struct bonding *bond); +int bond_alb_set_mac_address(struct net_device *dev, void *addr); #endif /* __BOND_ALB_H__ */ diff -Nru a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c --- a/drivers/net/bonding/bond_main.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/bonding/bond_main.c Wed Aug 27 18:30:33 2003 @@ -278,7 +278,7 @@ * bonding round-robin mode ignoring links after failover/recovery * * 2003/03/17 - Jay Vosburgh - * - kmalloc fix (GPF_KERNEL to GPF_ATOMIC) reported by + * - kmalloc fix (GFP_KERNEL to GFP_ATOMIC) reported by * Shmulik dot Hen at intel.com. * - Based on discussion on mailing list, changed use of * update_slave_cnt(), created wrapper functions for adding/removing @@ -323,22 +323,22 @@ * 2003/03/18 - Amir Noam , * Tsippy Mendelson and * Shmulik Hen - * - Added support for IEEE 802.3ad Dynamic link aggregation mode. + * - Added support for IEEE 802.3ad Dynamic link aggregation mode. * * 2003/05/01 - Amir Noam - * - Added ABI version control to restore compatibility between - * new/old ifenslave and new/old bonding. + * - Added ABI version control to restore compatibility between + * new/old ifenslave and new/old bonding. * * 2003/05/01 - Shmulik Hen - * - Fixed bug in bond_release_all(): save old value of current_slave - * before setting it to NULL. - * - Changed driver versioning scheme to include version number instead - * of release date (that is already in another field). There are 3 - * fields X.Y.Z where: - * X - Major version - big behavior changes - * Y - Minor version - addition of features - * Z - Extra version - minor changes and bug fixes - * The current version is 1.0.0 as a base line. + * - Fixed bug in bond_release_all(): save old value of current_slave + * before setting it to NULL. + * - Changed driver versioning scheme to include version number instead + * of release date (that is already in another field). There are 3 + * fields X.Y.Z where: + * X - Major version - big behavior changes + * Y - Minor version - addition of features + * Z - Extra version - minor changes and bug fixes + * The current version is 1.0.0 as a base line. * * 2003/05/01 - Tsippy Mendelson and * Amir Noam @@ -382,12 +382,44 @@ * ABI version if receiving unsupported ioctl commands. * * 2003/05/22 - Jay Vosburgh + * - Fix ifenslave -c causing bond to loose existing routes; + * added bond_set_mac_address() that doesn't require the + * bond to be down. * - In conjunction with fix for ifenslave -c, in * bond_change_active(), changing to the already active slave * is no longer an error (it successfully does nothing). * * 2003/06/30 - Amir Noam * - Fixed bond_change_active() for ALB/TLB modes. + * Version to 2.2.14. + * + * 2003/07/29 - Amir Noam + * - Fixed ARP monitoring bug. + * Version to 2.2.15. + * + * 2003/07/31 - Willy Tarreau + * - Fixed kernel panic when using ARP monitoring without + * setting bond's IP address. + * Version to 2.2.16. + * + * 2003/08/06 - Amir Noam + * - Back port from 2.6: use alloc_netdev(); fix /proc handling; + * made stats a part of bond struct so no need to allocate + * and free it separately; use standard list operations instead + * of pre-allocated array of bonds. + * Version to 2.3.0. + * + * 2003/08/07 - Jay Vosburgh , + * Amir Noam and + * Shmulik Hen + * - Propagating master's settings: Distinguish between modes that + * use a primary slave from those that don't, and propagate settings + * accordingly; Consolidate change_active opeartions and add + * reselect_active and find_best opeartions; Decouple promiscuous + * handling from the multicast mode setting; Add support for changing + * HW address and MTU with proper unwind; Consolidate procfs code, + * add CHANGENAME handler; Enhance netdev notification handling. + * Version to 2.4.0. */ #include @@ -432,10 +464,10 @@ #include "bond_3ad.h" #include "bond_alb.h" -#define DRV_VERSION "2.2.14" -#define DRV_RELDATE "June 30, 2003" -#define DRV_NAME "bonding" -#define DRV_DESCRIPTION "Ethernet Channel Bonding Driver" +#define DRV_VERSION "2.4.0" +#define DRV_RELDATE "August 7, 2003" +#define DRV_NAME "bonding" +#define DRV_DESCRIPTION "Ethernet Channel Bonding Driver" static const char *version = DRV_NAME ".c:v" DRV_VERSION " (" DRV_RELDATE ")\n"; @@ -453,6 +485,11 @@ #define MAX_ARP_IP_TARGETS 16 #endif +#define USES_PRIMARY(mode) \ + (((mode) == BOND_MODE_ACTIVEBACKUP) || \ + ((mode) == BOND_MODE_TLB) || \ + ((mode) == BOND_MODE_ALB)) + struct bond_parm_tbl { char *modename; int mode; @@ -515,9 +552,7 @@ { NULL, -1}, }; -static int first_pass = 1; -static struct bonding *these_bonds = NULL; -static struct net_device *dev_bonds = NULL; +static LIST_HEAD(bond_dev_list); MODULE_PARM(max_bonds, "i"); MODULE_PARM_DESC(max_bonds, "Max number of bonded devices"); @@ -549,7 +584,6 @@ static void bond_mii_monitor(struct net_device *dev); static void loadbalance_arp_monitor(struct net_device *dev); static void activebackup_arp_monitor(struct net_device *dev); -static int bond_event(struct notifier_block *this, unsigned long event, void *ptr); static void bond_mc_list_destroy(struct bonding *bond); static void bond_mc_add(bonding_t *bond, void *addr, int alen); static void bond_mc_delete(bonding_t *bond, void *addr, int alen); @@ -563,30 +597,17 @@ static int bond_release(struct net_device *master, struct net_device *slave); static int bond_release_all(struct net_device *master); static int bond_sethwaddr(struct net_device *master, struct net_device *slave); +static void change_active_interface(struct bonding *bond, struct slave *new); +static void reselect_active_interface(struct bonding *bond); +static struct slave *find_best_interface(struct bonding *bond); -/* - * bond_get_info is the interface into the /proc filesystem. This is - * a different interface than the BOND_INFO_QUERY ioctl. That is done - * through the generic networking ioctl interface, and bond_info_query - * is the internal function which provides that information. - */ -static int bond_get_info(char *buf, char **start, off_t offset, int length); - -/* Caller must hold bond->ptrlock for write */ -static inline struct slave* -bond_assign_current_slave(struct bonding *bond,struct slave *newslave) -{ - if ((bond_mode == BOND_MODE_TLB) || - (bond_mode == BOND_MODE_ALB)) { - bond_alb_assign_current_slave(bond, newslave); - } else { - bond->current_slave = newslave; - } - - return bond->current_slave; -} /* #define BONDING_DEBUG 1 */ +#ifdef BONDING_DEBUG +#define dprintk(x...) printk(x...) +#else /* BONDING_DEBUG */ +#define dprintk(x...) do {} while (0) +#endif /* BONDING_DEBUG */ /* several macros */ @@ -675,26 +696,13 @@ BUG(); } -/* - * Set MAC. Differs from eth_mac_addr in that we allow changes while - * netif_running(). - */ -static int -bond_set_mac_address(struct net_device *dev, void *p) -{ - struct sockaddr *addr = p; - - memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); - return 0; -} - /* * This function detaches the slave from the list . * WARNING: no check is made to verify if the slave effectively * belongs to . It returns in case it's needed. * Nothing is freed on return, structures are just unchained. * If the bond->current_slave pointer was pointing to , - * it's replaced with bond->next, or NULL if not applicable. + * it should be changed by the calling function. * * bond->lock held for writing by caller. */ @@ -728,17 +736,6 @@ update_slave_cnt(bond, -1); - /* no need to hold ptrlock since bond lock is - * already held for writing - */ - if (slave == bond->current_slave) { - if ( bond->next != (slave_t *)bond) { /* found one slave */ - bond_assign_current_slave(bond, bond->next); - } else { - bond_assign_current_slave(bond, NULL); - } - } - return slave; } @@ -1173,23 +1170,22 @@ } /* - * Push the promiscuity flag down to all slaves + * Push the promiscuity flag down to appropriate slaves */ static void bond_set_promiscuity(bonding_t *bond, int inc) { slave_t *slave; - switch (multicast_mode) { - case BOND_MULTICAST_ACTIVE : - /* write lock already acquired */ - if (bond->current_slave != NULL) + + if (USES_PRIMARY(bond_mode)) { + if (bond->current_slave) { dev_set_promiscuity(bond->current_slave->dev, inc); - break; - case BOND_MULTICAST_ALL : - for (slave = bond->prev; slave != (slave_t*)bond; slave = slave->prev) + } + + } else { + for (slave = bond->prev; slave != (slave_t*)bond; + slave = slave->prev) { dev_set_promiscuity(slave->dev, inc); - break; - case BOND_MULTICAST_DISABLED : - break; + } } } @@ -1235,20 +1231,23 @@ bonding_t *bond = master->priv; struct dev_mc_list *dmi; - if (multicast_mode == BOND_MULTICAST_DISABLED) - return; - /* - * Lock the private data for the master - */ write_lock_bh(&bond->lock); - /* set promiscuity flag to slaves */ + /* + * Do promisc before checking multicast_mode + */ if ( (master->flags & IFF_PROMISC) && !(bond->flags & IFF_PROMISC) ) bond_set_promiscuity(bond, 1); if ( !(master->flags & IFF_PROMISC) && (bond->flags & IFF_PROMISC) ) bond_set_promiscuity(bond, -1); + if (multicast_mode == BOND_MULTICAST_DISABLED) { + bond->flags = master->flags; + write_unlock_bh(&bond->lock); + return; + } + /* set allmulti flag to slaves */ if ( (master->flags & IFF_ALLMULTI) && !(bond->flags & IFF_ALLMULTI) ) bond_set_allmulti(bond, 1); @@ -1280,32 +1279,40 @@ /* * Update the mc list and multicast-related flags for the new and - * old active slaves (if any) according to the multicast mode + * old active slaves (if any) according to the multicast mode, and + * promiscuous flags unconditionally. */ static void bond_mc_update(bonding_t *bond, slave_t *new, slave_t *old) { struct dev_mc_list *dmi; - switch(multicast_mode) { - case BOND_MULTICAST_ACTIVE : + if (USES_PRIMARY(bond_mode)) { if (bond->device->flags & IFF_PROMISC) { - if (old != NULL && new != old) + if (old) dev_set_promiscuity(old->dev, -1); - dev_set_promiscuity(new->dev, 1); + if (new) + dev_set_promiscuity(new->dev, 1); } + } + + switch(multicast_mode) { + case BOND_MULTICAST_ACTIVE : if (bond->device->flags & IFF_ALLMULTI) { - if (old != NULL && new != old) + if (old) dev_set_allmulti(old->dev, -1); - dev_set_allmulti(new->dev, 1); + if (new) + dev_set_allmulti(new->dev, 1); } /* first remove all mc addresses from old slave if any, and _then_ add them to new active slave */ - if (old != NULL && new != old) { + if (old) { for (dmi = bond->device->mc_list; dmi != NULL; dmi = dmi->next) dev_mc_delete(old->dev, dmi->dmi_addr, dmi->dmi_addrlen, 0); } - for (dmi = bond->device->mc_list; dmi != NULL; dmi = dmi->next) - dev_mc_add(new->dev, dmi->dmi_addr, dmi->dmi_addrlen, 0); + if (new) { + for (dmi = bond->device->mc_list; dmi != NULL; dmi = dmi->next) + dev_mc_add(new->dev, dmi->dmi_addr, dmi->dmi_addrlen, 0); + } break; case BOND_MULTICAST_ALL : /* nothing to do: mc list is already up-to-date on all slaves */ @@ -1421,7 +1428,7 @@ * The application already set the master's * mac address to that of the first slave */ - memcpy(addr.sa_data, master_dev->dev_addr, ETH_ALEN); + memcpy(addr.sa_data, master_dev->dev_addr, master_dev->addr_len); addr.sa_family = slave_dev->type; err = slave_dev->set_mac_address(slave_dev, &addr); if (err) { @@ -1467,11 +1474,19 @@ } } - if (multicast_mode == BOND_MULTICAST_ALL) { - /* set promiscuity level to new slave */ - if (master_dev->flags & IFF_PROMISC) + /* set promiscuity level to new slave */ + if (master_dev->flags & IFF_PROMISC) { + /* If the mode USES_PRIMARY, then the new slave gets the + * master's promisc (and mc) settings only if it becomes the + * current_slave, and that is taken care of later when calling + * bond_change_active() + */ + if (!USES_PRIMARY(bond_mode)) { dev_set_promiscuity(slave_dev, 1); + } + } + if (multicast_mode == BOND_MULTICAST_ALL) { /* set allmulti level to new slave */ if (master_dev->flags & IFF_ALLMULTI) dev_set_allmulti(slave_dev, 1); @@ -1584,9 +1599,7 @@ #endif /* first slave or no active slave yet, and this link is OK, so make this interface the active one */ - bond_assign_current_slave(bond, new_slave); - bond_set_slave_active_flags(new_slave); - bond_mc_update(bond, new_slave, NULL); + change_active_interface(bond, new_slave); } else { #ifdef BONDING_DEBUG @@ -1636,7 +1649,7 @@ /* first slave or no active slave yet, and this link * is OK, so make this interface the active one */ - bond_assign_current_slave(bond, new_slave); + change_active_interface(bond, new_slave); } /* if there is a primary slave, remember it */ @@ -1651,8 +1664,13 @@ #endif /* always active in trunk mode */ new_slave->state = BOND_STATE_ACTIVE; + + /* In trunking mode there is little meaning to current_slave + * anyway (it holds no special properties of the bond device), + * so we can change it without calling change_active_interface() + */ if (bond->current_slave == NULL) - bond_assign_current_slave(bond, new_slave); + bond->current_slave = new_slave; } write_unlock_bh(&bond->lock); @@ -1743,6 +1761,13 @@ return -ENODEV; } + /* Verify that master_dev is indeed the master of slave_dev */ + if (!(slave_dev->flags & IFF_SLAVE) || + (slave_dev->master != master_dev)) { + + return -EINVAL; + } + bond = (struct bonding *) master_dev->priv; write_lock_bh(&bond->lock); slave = (slave_t *)bond; @@ -1767,16 +1792,7 @@ (oldactive != NULL)&& (newactive->link == BOND_LINK_UP)&& IS_UP(newactive->dev)) { - if (bond_mode == BOND_MODE_ACTIVEBACKUP) { - bond_set_slave_inactive_flags(oldactive); - bond_set_slave_active_flags(newactive); - } - - bond_mc_update(bond, newactive, oldactive); - bond_assign_current_slave(bond, newactive); - printk("%s : activate %s(old : %s)\n", - master_dev->name, newactive->dev->name, - oldactive->dev->name); + change_active_interface(bond, newactive); } else { ret = -EINVAL; } @@ -1784,47 +1800,26 @@ return ret; } -/* Choose a new valid interface from the pool, set it active - * and make it the current slave. If no valid interface is - * found, the oldest slave in BACK state is choosen and - * activated. If none is found, it's considered as no - * interfaces left so the current slave is set to NULL. - * The result is a pointer to the current slave. - * - * Since this function sends messages tails through printk, the caller - * must have started something like `printk(KERN_INFO "xxxx ");'. +/** + * find_best_interface - select the best available slave to be the active one + * @bond: our bonding struct * * Warning: Caller must hold ptrlock for writing. */ -slave_t *change_active_interface(bonding_t *bond) +static struct slave *find_best_interface(struct bonding *bond) { - slave_t *newslave, *oldslave; - slave_t *bestslave = NULL; + struct slave *newslave, *oldslave; + struct slave *bestslave = NULL; int mintime; newslave = oldslave = bond->current_slave; if (newslave == NULL) { /* there were no active slaves left */ if (bond->next != (slave_t *)bond) { /* found one slave */ - newslave = bond_assign_current_slave(bond, bond->next); + newslave = bond->next; } else { - - printk (" but could not find any %s interface.\n", - (bond_mode == BOND_MODE_ACTIVEBACKUP) ? "backup":"other"); - bond_assign_current_slave(bond, NULL); return NULL; /* still no slave, return NULL */ } - } else if (bond_mode == BOND_MODE_ACTIVEBACKUP) { - /* make sure oldslave doesn't send arps - this could - * cause a ping-pong effect between interfaces since they - * would be able to tx arps - in active backup only one - * slave should be able to tx arps, and that should be - * the current_slave; the only exception is when all - * slaves have gone down, then only one non-current slave can - * send arps at a time; clearing oldslaves' mc list is handled - * later in this function. - */ - bond_set_slave_inactive_flags(oldslave); } mintime = updelay; @@ -1839,22 +1834,12 @@ newslave = bond->primary_slave; } + /* remember where to stop iterating over the slaves */ + oldslave = newslave; + do { if (IS_UP(newslave->dev)) { if (newslave->link == BOND_LINK_UP) { - /* this one is immediately usable */ - if (bond_mode == BOND_MODE_ACTIVEBACKUP) { - bond_set_slave_active_flags(newslave); - bond_mc_update(bond, newslave, oldslave); - printk (" and making interface %s the active one.\n", - newslave->dev->name); - } - else { - printk (" and setting pointer to interface %s.\n", - newslave->dev->name); - } - - bond_assign_current_slave(bond, newslave); return newslave; } else if (newslave->link == BOND_LINK_BACK) { @@ -1867,46 +1852,105 @@ } } while ((newslave = newslave->next) != oldslave); - /* no usable backup found, we'll see if we at least got a link that was - coming back for a long time, and could possibly already be usable. - */ - - if (bestslave != NULL) { - /* early take-over. */ - printk (" and making interface %s the active one %d ms earlier.\n", - bestslave->dev->name, - (updelay - bestslave->delay)*miimon); - - bestslave->delay = 0; - bestslave->link = BOND_LINK_UP; - bestslave->jiffies = jiffies; - bond_set_slave_active_flags(bestslave); - bond_mc_update(bond, bestslave, oldslave); - bond_assign_current_slave(bond, bestslave); - return bestslave; - } - - if ((bond_mode == BOND_MODE_ACTIVEBACKUP) && - (multicast_mode == BOND_MULTICAST_ACTIVE) && - (oldslave != NULL)) { - /* flush bonds (master's) mc_list from oldslave since it wasn't - * updated (and deleted) above - */ - bond_mc_list_flush(oldslave->dev, bond->device); - if (bond->device->flags & IFF_PROMISC) { - dev_set_promiscuity(oldslave->dev, -1); + return bestslave; +} + +/** + * change_active_interface - change the active slave into the specified one + * @bond: our bonding struct + * @new: the new slave to make the active one + * + * Set the new slave to the bond's settings and unset them on the old + * current_slave. + * Setting include flags, mc-list, promiscuity, allmulti, etc. + * + * If @new's link state is %BOND_LINK_BACK we'll set it to %BOND_LINK_UP, + * because it is apparently the best available slave we have, even though its + * updelay hasn't timed out yet. + * + * Warning: Caller must hold ptrlock for writing. + */ +static void change_active_interface(struct bonding *bond, struct slave *new) +{ + struct slave *old = bond->current_slave; + + if (old == new) { + return; + } + + if (new) { + if (new->link == BOND_LINK_BACK) { + if (USES_PRIMARY(bond_mode)) { + printk (KERN_INFO + "%s: making interface %s the new " + "active one %d ms earlier.\n", + bond->device->name, new->dev->name, + (updelay - new->delay) * miimon); + } + + new->delay = 0; + new->link = BOND_LINK_UP; + new->jiffies = jiffies; + + if (bond_mode == BOND_MODE_8023AD) { + bond_3ad_handle_link_change(new, BOND_LINK_UP); + } + + if ((bond_mode == BOND_MODE_TLB) || + (bond_mode == BOND_MODE_ALB)) { + bond_alb_handle_link_change(bond, new, BOND_LINK_UP); + } + } else { + if (USES_PRIMARY(bond_mode)) { + printk (KERN_INFO + "%s: making interface %s the new active one.\n", + bond->device->name, new->dev->name); + } } - if (bond->device->flags & IFF_ALLMULTI) { - dev_set_allmulti(oldslave->dev, -1); + } + + if (bond_mode == BOND_MODE_ACTIVEBACKUP) { + if (old) { + bond_set_slave_inactive_flags(old); + } + + if (new) { + bond_set_slave_active_flags(new); } } - printk (" but could not find any %s interface.\n", - (bond_mode == BOND_MODE_ACTIVEBACKUP) ? "backup":"other"); - - /* absolutely nothing found. let's return NULL */ - bond_assign_current_slave(bond, NULL); - return NULL; + if (USES_PRIMARY(bond_mode)) { + bond_mc_update(bond, new, old); + } + + if ((bond_mode == BOND_MODE_TLB) || + (bond_mode == BOND_MODE_ALB)) { + bond_alb_assign_current_slave(bond, new); + } else { + bond->current_slave = new; + } +} + +/** + * reselect_active_interface - select a new active slave, if needed + * @bond: our bonding struct + * + * This functions shoud be called when one of the following occurs: + * - The old current_slave has been released or lost its link. + * - The primary_slave has got its link back. + * - A slave has got its link back and there's no old current_slave. + * + * Warning: Caller must hold ptrlock for writing. + */ +static void reselect_active_interface(struct bonding *bond) +{ + struct slave *best_slave; + + best_slave = find_best_interface(bond); + + if (best_slave != bond->current_slave) { + change_active_interface(bond, best_slave); + } } /* @@ -1955,12 +1999,12 @@ "of %s to a different address " "to avoid conflicts.\n", slave->name, - slave->dev_addr[0], - slave->dev_addr[1], - slave->dev_addr[2], - slave->dev_addr[3], - slave->dev_addr[4], - slave->dev_addr[5], + our_slave->perm_hwaddr[0], + our_slave->perm_hwaddr[1], + our_slave->perm_hwaddr[2], + our_slave->perm_hwaddr[3], + our_slave->perm_hwaddr[4], + our_slave->perm_hwaddr[5], bond->device->name, slave->name); } @@ -1973,6 +2017,11 @@ bond_3ad_unbind_slave(our_slave); } + printk (KERN_INFO "%s: releasing %s interface %s\n", + master->name, + (our_slave->state == BOND_STATE_ACTIVE) ? "active" : "backup", + slave->name); + /* release the slave from its bond */ bond_detach_slave(bond, our_slave); @@ -1980,18 +2029,11 @@ bond->primary_slave = NULL; } - printk (KERN_INFO "%s: releasing %s interface %s", - master->name, - (our_slave->state == BOND_STATE_ACTIVE) ? "active" : "backup", - slave->name); - - if (our_slave == old_current) { - /* find a new interface and be verbose */ - change_active_interface(bond); - } else { - printk(".\n"); + if (bond->current_slave == our_slave) { + change_active_interface(bond, NULL); + reselect_active_interface(bond); } - + if (bond->current_slave == NULL) { printk(KERN_INFO "%s: now running without any active interface !\n", @@ -2019,16 +2061,22 @@ return -EINVAL; } + /* unset promiscuity level from slave */ + if (master->flags & IFF_PROMISC) { + /* If the mode USES_PRIMARY, then we should only remove its + * promisc settings if it was the current_slave, but that was + * already taken care of above when we detached the slave + */ + if (!USES_PRIMARY(bond_mode)) { + dev_set_promiscuity(slave, -1); + } + } + /* undo settings and restore original values */ - if (multicast_mode == BOND_MULTICAST_ALL) { /* flush master's mc_list from slave */ bond_mc_list_flush (slave, master); - /* unset promiscuity level from slave */ - if (master->flags & IFF_PROMISC) - dev_set_promiscuity(slave, -1); - /* unset allmulti level from slave */ if (master->flags & IFF_ALLMULTI) dev_set_allmulti(slave, -1); @@ -2095,7 +2143,7 @@ } old_current = bond->current_slave; - bond_assign_current_slave(bond, NULL); + change_active_interface(bond, NULL); bond->current_arp_slave = NULL; bond->primary_slave = NULL; @@ -2124,17 +2172,17 @@ */ write_unlock_bh(&bond->lock); - if (multicast_mode == BOND_MULTICAST_ALL - || (multicast_mode == BOND_MULTICAST_ACTIVE - && old_current == our_slave)) { + /* unset promiscuity level from slave */ + if (master->flags & IFF_PROMISC) { + if (!USES_PRIMARY(bond_mode)) { + dev_set_promiscuity(slave_dev, -1); + } + } + if (multicast_mode == BOND_MULTICAST_ALL) { /* flush master's mc_list from slave */ bond_mc_list_flush (slave_dev, master); - /* unset promiscuity level from slave */ - if (master->flags & IFF_PROMISC) - dev_set_promiscuity(slave_dev, -1); - /* unset allmulti level from slave */ if (master->flags & IFF_ALLMULTI) dev_set_allmulti(slave_dev, -1); @@ -2183,8 +2231,9 @@ static void bond_mii_monitor(struct net_device *master) { bonding_t *bond = (struct bonding *) master->priv; - slave_t *slave, *bestslave, *oldcurrent; + slave_t *slave, *oldcurrent; int slave_died = 0; + int do_failover = 0; read_lock(&bond->lock); @@ -2194,7 +2243,6 @@ * program could monitor the link itself if needed. */ - bestslave = NULL; slave = (slave_t *)bond; read_lock(&bond->ptrlock); @@ -2202,8 +2250,6 @@ read_unlock(&bond->ptrlock); while ((slave = slave->prev) != (slave_t *)bond) { - /* use updelay+1 to match an UP slave even when updelay is 0 */ - int mindelay = updelay + 1; struct net_device *dev = slave->dev; int link_state; u16 old_speed = slave->speed; @@ -2214,14 +2260,7 @@ switch (slave->link) { case BOND_LINK_UP: /* the link was up */ if (link_state == BMSR_LSTATUS) { - /* link stays up, tell that this one - is immediately available */ - if (IS_UP(dev) && (mindelay > -2)) { - /* -2 is the best case : - this slave was already up */ - mindelay = -2; - bestslave = slave; - } + /* link stays up, nothing more to do */ break; } else { /* link going down */ @@ -2261,6 +2300,7 @@ (bond_mode == BOND_MODE_8023AD)) { bond_set_slave_inactive_flags(slave); } + printk(KERN_INFO "%s: link status definitely down " "for interface %s, disabling it", @@ -2277,14 +2317,10 @@ bond_alb_handle_link_change(bond, slave, BOND_LINK_DOWN); } - write_lock(&bond->ptrlock); - if (slave == bond->current_slave) { - /* find a new interface and be verbose */ - change_active_interface(bond); - } else { - printk(".\n"); + if (slave == oldcurrent) { + do_failover = 1; } - write_unlock(&bond->ptrlock); + slave_died = 1; } else { slave->delay--; @@ -2299,13 +2335,6 @@ master->name, (downdelay - slave->delay) * miimon, dev->name); - - if (IS_UP(dev) && (mindelay > -1)) { - /* -1 is a good case : this slave went - down only for a short time */ - mindelay = -1; - bestslave = slave; - } } break; case BOND_LINK_DOWN: /* the link was down */ @@ -2375,26 +2404,12 @@ bond_alb_handle_link_change(bond, slave, BOND_LINK_UP); } - write_lock(&bond->ptrlock); - if ( (bond->primary_slave != NULL) - && (slave == bond->primary_slave) ) - change_active_interface(bond); - write_unlock(&bond->ptrlock); - } - else + if ((oldcurrent == NULL) || + (slave == bond->primary_slave)) { + do_failover = 1; + } + } else { slave->delay--; - - /* we'll also look for the mostly eligible slave */ - if (bond->primary_slave == NULL) { - if (IS_UP(dev) && (slave->delay < mindelay)) { - mindelay = slave->delay; - bestslave = slave; - } - } else if ( (IS_UP(bond->primary_slave->dev)) || - ( (!IS_UP(bond->primary_slave->dev)) && - (IS_UP(dev) && (slave->delay < mindelay)) ) ) { - mindelay = slave->delay; - bestslave = slave; } } break; @@ -2413,58 +2428,17 @@ } /* end of while */ - /* - * if there's no active interface and we discovered that one - * of the slaves could be activated earlier, so we do it. - */ - read_lock(&bond->ptrlock); - oldcurrent = bond->current_slave; - read_unlock(&bond->ptrlock); - - /* no active interface at the moment or need to bring up the primary */ - if (oldcurrent == NULL) { /* no active interface at the moment */ - if (bestslave != NULL) { /* last chance to find one ? */ - if (bestslave->link == BOND_LINK_UP) { - printk (KERN_INFO - "%s: making interface %s the new active one.\n", - master->name, bestslave->dev->name); - } else { - printk (KERN_INFO - "%s: making interface %s the new " - "active one %d ms earlier.\n", - master->name, bestslave->dev->name, - (updelay - bestslave->delay) * miimon); + if (do_failover) { + write_lock(&bond->ptrlock); - bestslave->delay = 0; - bestslave->link = BOND_LINK_UP; - bestslave->jiffies = jiffies; - - /* notify ad that the link status has changed */ - if (bond_mode == BOND_MODE_8023AD) { - bond_3ad_handle_link_change(bestslave, BOND_LINK_UP); - } - - if ((bond_mode == BOND_MODE_TLB) || - (bond_mode == BOND_MODE_ALB)) { - bond_alb_handle_link_change(bond, bestslave, BOND_LINK_UP); - } - } - - if (bond_mode == BOND_MODE_ACTIVEBACKUP) { - bond_set_slave_active_flags(bestslave); - bond_mc_update(bond, bestslave, NULL); - } else if (bond_mode != BOND_MODE_8023AD) { - bestslave->state = BOND_STATE_ACTIVE; - } - write_lock(&bond->ptrlock); - bond_assign_current_slave(bond, bestslave); - write_unlock(&bond->ptrlock); - } else if (slave_died) { - /* print this message only once a slave has just died */ + reselect_active_interface(bond); + if (oldcurrent && !bond->current_slave) { printk(KERN_INFO "%s: now running without any active interface !\n", master->name); } + + write_unlock(&bond->ptrlock); } read_unlock(&bond->lock); @@ -2482,9 +2456,10 @@ static void loadbalance_arp_monitor(struct net_device *master) { bonding_t *bond; - slave_t *slave; + slave_t *slave, *oldcurrent; int the_delta_in_ticks = arp_interval * HZ / 1000; int next_timer = jiffies + (arp_interval * HZ / 1000); + int do_failover = 0; bond = (struct bonding *) master->priv; if (master->priv == NULL) { @@ -2508,6 +2483,10 @@ read_lock(&bond->lock); + read_lock(&bond->ptrlock); + oldcurrent = bond->current_slave; + read_unlock(&bond->ptrlock); + /* see if any of the previous devices are up now (i.e. they have * xmt and rcv traffic). the current_slave does not come into * the picture unless it is null. also, slave->jiffies is not needed @@ -2534,21 +2513,19 @@ * current_slave being null after enslaving * is closed. */ - write_lock(&bond->ptrlock); - if (bond->current_slave == NULL) { + if (oldcurrent == NULL) { printk(KERN_INFO "%s: link status definitely up " "for interface %s, ", master->name, slave->dev->name); - change_active_interface(bond); + do_failover = 1; } else { printk(KERN_INFO "%s: interface %s is now up\n", master->name, slave->dev->name); } - write_unlock(&bond->ptrlock); } } else { /* slave->link == BOND_LINK_UP */ @@ -2571,11 +2548,9 @@ master->name, slave->dev->name); - write_lock(&bond->ptrlock); - if (slave == bond->current_slave) { - change_active_interface(bond); + if (slave == oldcurrent) { + do_failover = 1; } - write_unlock(&bond->ptrlock); } } @@ -2591,6 +2566,19 @@ } } + if (do_failover) { + write_lock(&bond->ptrlock); + + reselect_active_interface(bond); + if (oldcurrent && !bond->current_slave) { + printk(KERN_INFO + "%s: now running without any active interface !\n", + master->name); + } + + write_unlock(&bond->ptrlock); + } + read_unlock(&bond->lock); rtnl_exunlock(); rtnl_shunlock(); @@ -2651,9 +2639,7 @@ if ((bond->current_slave == NULL) && ((jiffies - slave->dev->trans_start) <= the_delta_in_ticks)) { - bond_assign_current_slave(bond, slave); - bond_set_slave_active_flags(slave); - bond_mc_update(bond, slave, NULL); + change_active_interface(bond, slave); bond->current_arp_slave = NULL; } else if (bond->current_slave != slave) { /* this slave has just come up but we @@ -2743,7 +2729,8 @@ master->name, slave->dev->name); write_lock(&bond->ptrlock); - slave = change_active_interface(bond); + reselect_active_interface(bond); + slave = bond->current_slave; write_unlock(&bond->ptrlock); bond->current_arp_slave = slave; if (slave != NULL) { @@ -2762,13 +2749,10 @@ bond->primary_slave->dev->name); /* primary is up so switch to it */ - bond_set_slave_inactive_flags(slave); - bond_mc_update(bond, bond->primary_slave, slave); write_lock(&bond->ptrlock); - bond_assign_current_slave(bond, bond->primary_slave); + change_active_interface(bond, bond->primary_slave); write_unlock(&bond->ptrlock); slave = bond->primary_slave; - bond_set_slave_active_flags(slave); slave->jiffies = jiffies; } else { bond->current_arp_slave = NULL; @@ -2811,7 +2795,7 @@ /* if the link state is up at this point, we * mark it down - this can happen if we have * simultaneous link failures and - * change_active_interface doesn't make this + * reselect_active_interface doesn't make this * one the current slave so it is still marked * up when it is actually down */ @@ -3056,15 +3040,9 @@ case SIOCBONDRELEASE: ret = bond_release(master_dev, slave_dev); break; - case BOND_SETHWADDR_OLD: - case SIOCBONDSETHWADDR: - ret = bond_sethwaddr(master_dev, slave_dev); - break; case BOND_CHANGE_ACTIVE_OLD: case SIOCBONDCHANGEACTIVE: - if ((bond_mode == BOND_MODE_ACTIVEBACKUP) || - (bond_mode == BOND_MODE_TLB) || - (bond_mode == BOND_MODE_ALB)) { + if (USES_PRIMARY(bond_mode)) { ret = bond_change_active(master_dev, slave_dev); } else { @@ -3186,7 +3164,7 @@ dev_queue_xmit(skb); write_lock(&bond->ptrlock); - bond_assign_current_slave(bond, slave->next); + bond->current_slave = slave->next; write_unlock(&bond->ptrlock); read_unlock(&bond->lock); @@ -3372,208 +3350,479 @@ return stats; } -static int bond_get_info(char *buf, char **start, off_t offset, int length) +#ifdef CONFIG_PROC_FS +static int bond_read_proc(char *buf, char **start, off_t off, int count, int *eof, void *data) { - bonding_t *bond = these_bonds; + struct bonding *bond = (struct bonding *) data; int len = 0; - off_t begin = 0; u16 link; slave_t *slave = NULL; + /* make sure the bond won't be taken away */ + read_lock(&dev_base_lock); + len += sprintf(buf + len, "%s\n", version); - while (bond != NULL) { - /* - * This function locks the mutex, so we can't lock it until - * afterwards - */ - link = bond_check_mii_link(bond); + /* + * This function locks the mutex, so we can't lock it until + * afterwards + */ + link = bond_check_mii_link(bond); - len += sprintf(buf + len, "Bonding Mode: %s\n", - bond_mode_name()); + len += sprintf(buf + len, "Bonding Mode: %s\n", + bond_mode_name()); - if ((bond_mode == BOND_MODE_ACTIVEBACKUP) || - (bond_mode == BOND_MODE_TLB) || - (bond_mode == BOND_MODE_ALB)) { - read_lock_bh(&bond->lock); - read_lock(&bond->ptrlock); - if (bond->current_slave != NULL) { - len += sprintf(buf + len, - "Currently Active Slave: %s\n", - bond->current_slave->dev->name); - } - read_unlock(&bond->ptrlock); - read_unlock_bh(&bond->lock); + if (USES_PRIMARY(bond_mode)) { + read_lock_bh(&bond->lock); + read_lock(&bond->ptrlock); + if (bond->current_slave != NULL) { + len += sprintf(buf + len, + "Currently Active Slave: %s\n", + bond->current_slave->dev->name); + } + read_unlock(&bond->ptrlock); + read_unlock_bh(&bond->lock); + } + + len += sprintf(buf + len, "MII Status: "); + len += sprintf(buf + len, + link == BMSR_LSTATUS ? "up\n" : "down\n"); + len += sprintf(buf + len, "MII Polling Interval (ms): %d\n", + miimon); + len += sprintf(buf + len, "Up Delay (ms): %d\n", + updelay * miimon); + len += sprintf(buf + len, "Down Delay (ms): %d\n", + downdelay * miimon); + len += sprintf(buf + len, "Multicast Mode: %s\n", + multicast_mode_name()); + + read_lock_bh(&bond->lock); + + if (bond_mode == BOND_MODE_8023AD) { + struct ad_info ad_info; + + len += sprintf(buf + len, "\n802.3ad info\n"); + + if (bond_3ad_get_active_agg_info(bond, &ad_info)) { + len += sprintf(buf + len, "bond %s has no active aggregator\n", bond->device->name); + } else { + len += sprintf(buf + len, "Active Aggregator Info:\n"); + + len += sprintf(buf + len, "\tAggregator ID: %d\n", ad_info.aggregator_id); + len += sprintf(buf + len, "\tNumber of ports: %d\n", ad_info.ports); + len += sprintf(buf + len, "\tActor Key: %d\n", ad_info.actor_key); + len += sprintf(buf + len, "\tPartner Key: %d\n", ad_info.partner_key); + len += sprintf(buf + len, "\tPartner Mac Address: %02x:%02x:%02x:%02x:%02x:%02x\n", + ad_info.partner_system[0], + ad_info.partner_system[1], + ad_info.partner_system[2], + ad_info.partner_system[3], + ad_info.partner_system[4], + ad_info.partner_system[5]); } + } + + for (slave = bond->prev; slave != (slave_t *)bond; + slave = slave->prev) { + len += sprintf(buf + len, "\nSlave Interface: %s\n", slave->dev->name); len += sprintf(buf + len, "MII Status: "); - len += sprintf(buf + len, - link == BMSR_LSTATUS ? "up\n" : "down\n"); - len += sprintf(buf + len, "MII Polling Interval (ms): %d\n", - miimon); - len += sprintf(buf + len, "Up Delay (ms): %d\n", - updelay * miimon); - len += sprintf(buf + len, "Down Delay (ms): %d\n", - downdelay * miimon); - len += sprintf(buf + len, "Multicast Mode: %s\n", - multicast_mode_name()); - read_lock_bh(&bond->lock); + len += sprintf(buf + len, + slave->link == BOND_LINK_UP ? + "up\n" : "down\n"); + len += sprintf(buf + len, "Link Failure Count: %d\n", + slave->link_failure_count); - if (bond_mode == BOND_MODE_8023AD) { - struct ad_info ad_info; + if (app_abi_ver >= 1) { + len += sprintf(buf + len, + "Permanent HW addr: %02x:%02x:%02x:%02x:%02x:%02x\n", + slave->perm_hwaddr[0], + slave->perm_hwaddr[1], + slave->perm_hwaddr[2], + slave->perm_hwaddr[3], + slave->perm_hwaddr[4], + slave->perm_hwaddr[5]); + } - len += sprintf(buf + len, "\n802.3ad info\n"); + if (bond_mode == BOND_MODE_8023AD) { + struct aggregator *agg = SLAVE_AD_INFO(slave).port.aggregator; - if (bond_3ad_get_active_agg_info(bond, &ad_info)) { - len += sprintf(buf + len, "bond %s has no active aggregator\n", bond->device->name); + if (agg) { + len += sprintf(buf + len, "Aggregator ID: %d\n", + agg->aggregator_identifier); } else { - len += sprintf(buf + len, "Active Aggregator Info:\n"); - - len += sprintf(buf + len, "\tAggregator ID: %d\n", ad_info.aggregator_id); - len += sprintf(buf + len, "\tNumber of ports: %d\n", ad_info.ports); - len += sprintf(buf + len, "\tActor Key: %d\n", ad_info.actor_key); - len += sprintf(buf + len, "\tPartner Key: %d\n", ad_info.partner_key); - len += sprintf(buf + len, "\tPartner Mac Address: %02x:%02x:%02x:%02x:%02x:%02x\n", - ad_info.partner_system[0], - ad_info.partner_system[1], - ad_info.partner_system[2], - ad_info.partner_system[3], - ad_info.partner_system[4], - ad_info.partner_system[5]); + len += sprintf(buf + len, "Aggregator ID: N/A\n"); } } + } + read_unlock_bh(&bond->lock); - for (slave = bond->prev; slave != (slave_t *)bond; - slave = slave->prev) { - len += sprintf(buf + len, "\nSlave Interface: %s\n", slave->dev->name); + /* + * Figure out the calcs for the /proc/net interface + */ + if (len <= off + count) { + *eof = 1; + } + *start = buf + off; + len -= off; + if (len > count) { + len = count; + } + if (len < 0) { + len = 0; + } - len += sprintf(buf + len, "MII Status: "); + read_unlock(&dev_base_lock); - len += sprintf(buf + len, - slave->link == BOND_LINK_UP ? - "up\n" : "down\n"); - len += sprintf(buf + len, "Link Failure Count: %d\n", - slave->link_failure_count); - - if (app_abi_ver >= 1) { - len += sprintf(buf + len, - "Permanent HW addr: %02x:%02x:%02x:%02x:%02x:%02x\n", - slave->perm_hwaddr[0], - slave->perm_hwaddr[1], - slave->perm_hwaddr[2], - slave->perm_hwaddr[3], - slave->perm_hwaddr[4], - slave->perm_hwaddr[5]); - } + return len; +} +#endif /* CONFIG_PROC_FS */ - if (bond_mode == BOND_MODE_8023AD) { - struct aggregator *agg = SLAVE_AD_INFO(slave).port.aggregator; +static int bond_create_proc_info(struct bonding *bond) +{ +#ifdef CONFIG_PROC_FS + struct net_device *dev = bond->device; - if (agg) { - len += sprintf(buf + len, "Aggregator ID: %d\n", - agg->aggregator_identifier); - } else { - len += sprintf(buf + len, "Aggregator ID: N/A\n"); - } - } + bond->bond_proc_dir = proc_mkdir(dev->name, proc_net); + if (bond->bond_proc_dir == NULL) { + printk(KERN_ERR "%s: Cannot init /proc/net/%s/\n", + dev->name, dev->name); + return -ENOMEM; + } + bond->bond_proc_dir->owner = THIS_MODULE; + + bond->bond_proc_info_file = + create_proc_read_entry("info", 0, bond->bond_proc_dir, + bond_read_proc, bond); + if (bond->bond_proc_info_file == NULL) { + printk(KERN_ERR "%s: Cannot init /proc/net/%s/info\n", + dev->name, dev->name); + remove_proc_entry(dev->name, proc_net); + return -ENOMEM; + } + bond->bond_proc_info_file->owner = THIS_MODULE; + + memcpy(bond->procdir_name, dev->name, IFNAMSIZ); +#endif /* CONFIG_PROC_FS */ + return 0; +} + +static void bond_destroy_proc_info(struct bonding *bond) +{ +#ifdef CONFIG_PROC_FS + remove_proc_entry("info", bond->bond_proc_dir); + remove_proc_entry(bond->procdir_name, proc_net); + memset(bond->procdir_name, 0, IFNAMSIZ); + bond->bond_proc_dir = NULL; +#endif /* CONFIG_PROC_FS */ +} + +/* + * Change HW address + * + * Note that many devices must be down to change the HW address, and + * downing the master releases all slaves. We can make bonds full of + * bonding devices to test this, however. + */ +static inline int +bond_set_mac_address(struct net_device *dev, void *addr) +{ + struct bonding *bond = dev->priv; + struct sockaddr *sa = addr, tmp_sa; + struct slave *slave; + int error; + + dprintk(KERN_INFO "bond_set_mac_address %p %s\n", dev, + dev->name); + + if (!is_valid_ether_addr(sa->sa_data)) { + return -EADDRNOTAVAIL; + } + + for (slave = bond->prev; slave != (struct slave *)bond; + slave = slave->prev) { + dprintk(KERN_INFO "bond_set_mac: slave %p %s\n", slave, + slave->dev->name); + if (slave->dev->set_mac_address == NULL) { + error = -EOPNOTSUPP; + dprintk(KERN_INFO "bond_set_mac EOPNOTSUPP %s\n", + slave->dev->name); + goto unwind; + } + + error = slave->dev->set_mac_address(slave->dev, addr); + if (error) { + /* TODO: consider downing the slave + * and retry ? + * User should expect communications + * breakage anyway until ARP finish + * updating, so... + */ + dprintk(KERN_INFO "bond_set_mac err %d %s\n", + error, slave->dev->name); + goto unwind; } - read_unlock_bh(&bond->lock); + } - /* - * Figure out the calcs for the /proc/net interface - */ - *start = buf + (offset - begin); - len -= (offset - begin); - if (len > length) { - len = length; + /* success */ + memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); + return 0; + +unwind: + memcpy(tmp_sa.sa_data, dev->dev_addr, dev->addr_len); + tmp_sa.sa_family = dev->type; + + for (slave = slave->next; slave != bond->next; + slave = slave->next) { + int tmp_error; + + tmp_error = slave->dev->set_mac_address(slave->dev, &tmp_sa); + if (tmp_error) { + dprintk(KERN_INFO "bond_set_mac_address: " + "unwind err %d dev %s\n", + tmp_error, slave->dev->name); } - if (len < 0) { - len = 0; + } + + return error; +} + +/* + * Change the MTU of all of a master's slaves to match the master + */ +static inline int +bond_change_mtu(struct net_device *dev, int newmtu) +{ + bonding_t *bond = dev->priv; + slave_t *slave; + int error; + + dprintk(KERN_INFO "CM: b %p nm %d\n", bond, newmtu); + for (slave = bond->prev; slave != (slave_t *)bond; + slave = slave->prev) { + dprintk(KERN_INFO "CM: s %p s->p %p c_m %p\n", slave, + slave->prev, slave->dev->change_mtu); + if (slave->dev->change_mtu) { + error = slave->dev->change_mtu(slave->dev, newmtu); + } else { + slave->dev->mtu = newmtu; + error = 0; + } + + if (error) { + /* If we failed to set the slave's mtu to the new value + * we must abort the operation even in ACTIVE_BACKUP + * mode, because if we allow the backup slaves to have + * different mtu values than the active slave we'll + * need to change their mtu when doing a failover. That + * means changing their mtu from timer context, which + * is probably not a good idea. + */ + dprintk(KERN_INFO "bond_change_mtu err %d %s\n", + error, slave->dev->name); + goto unwind; } + } + + dev->mtu = newmtu; + return 0; - bond = bond->next_bond; +unwind: + for (slave = slave->next; slave != bond->next; + slave = slave->next) { + + if (slave->dev->change_mtu) { + slave->dev->change_mtu(slave->dev, dev->mtu); + } else { + slave->dev->mtu = dev->mtu; + } } - return len; + + return error; } -static int bond_event(struct notifier_block *this, unsigned long event, - void *ptr) +/* + * Change device name + */ +static inline int bond_event_changename(struct bonding *bond) { - struct bonding *this_bond = (struct bonding *)these_bonds; - struct bonding *last_bond; - struct net_device *event_dev = (struct net_device *)ptr; + int error; - /* while there are bonds configured */ - while (this_bond != NULL) { - if (this_bond == event_dev->priv ) { - switch (event) { - case NETDEV_UNREGISTER: - /* - * remove this bond from a linked list of - * bonds - */ - if (this_bond == these_bonds) { - these_bonds = this_bond->next_bond; - } else { - for (last_bond = these_bonds; - last_bond != NULL; - last_bond = last_bond->next_bond) { - if (last_bond->next_bond == - this_bond) { - last_bond->next_bond = - this_bond->next_bond; - } - } - } - return NOTIFY_DONE; + bond_destroy_proc_info(bond); + error = bond_create_proc_info(bond); + if (error) { + return NOTIFY_BAD; + } + return NOTIFY_DONE; +} - default: - return NOTIFY_DONE; - } - } else if (this_bond->device == event_dev->master) { - switch (event) { - case NETDEV_UNREGISTER: - bond_release(this_bond->device, event_dev); - break; - } - return NOTIFY_DONE; +static int bond_master_netdev_event(unsigned long event, struct net_device *event_dev) +{ + struct bonding *bond, *event_bond = NULL; + + list_for_each_entry(bond, &bond_dev_list, bond_list) { + if (bond == event_dev->priv) { + event_bond = bond; + break; } - this_bond = this_bond->next_bond; } + + if (event_bond == NULL) { + return NOTIFY_DONE; + } + + switch (event) { + case NETDEV_CHANGENAME: + return bond_event_changename(event_bond); + case NETDEV_UNREGISTER: + /* + * TODO: remove a bond from the list? + */ + break; + default: + break; + } + return NOTIFY_DONE; } +static int bond_slave_netdev_event(unsigned long event, struct net_device *event_dev) +{ + struct net_device *master = event_dev->master; + + switch (event) { + case NETDEV_UNREGISTER: + if (master != NULL) { + bond_release(master, event_dev); + } + break; + case NETDEV_CHANGE: + /* + * TODO: is this what we get if somebody + * sets up a hierarchical bond, then rmmod's + * one of the slave bonding devices? + */ + break; + case NETDEV_DOWN: + /* + * ... Or is it this? + */ + break; + case NETDEV_CHANGEMTU: + /* + * TODO: Should slaves be allowed to + * independently alter their MTU? For + * an active-backup bond, slaves need + * not be the same type of device, so + * MTUs may vary. For other modes, + * slaves arguably should have the + * same MTUs. To do this, we'd need to + * take over the slave's change_mtu + * function for the duration of their + * servitude. + */ + break; + case NETDEV_CHANGENAME: + /* + * TODO: handle changing the primary's name + */ + break; + default: + break; + } + + return NOTIFY_DONE; +} + +/* + * bond_netdev_event: handle netdev notifier chain events. + * + * This function receives events for the netdev chain. The caller (an + * ioctl handler calling notifier_call_chain) holds the necessary + * locks for us to safely manipulate the slave devices (RTNL lock, + * dev_probe_lock). + */ +static int bond_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) +{ + struct net_device *event_dev = (struct net_device *)ptr; + unsigned short flags; + int res = NOTIFY_DONE; + + dprintk(KERN_INFO "bond_netdev_event n_b %p ev %lx ptr %p\n", + this, event, ptr); + + flags = event_dev->flags & (IFF_MASTER | IFF_SLAVE); + switch (flags) { + case IFF_MASTER: + res = bond_master_netdev_event(event, event_dev); + break; + case IFF_SLAVE: + res = bond_slave_netdev_event(event, event_dev); + break; + default: + /* A master that is also a slave ? */ + break; + } + + return res; +} + static struct notifier_block bond_netdev_notifier = { - notifier_call: bond_event, + notifier_call: bond_netdev_event, }; +static void bond_deinit(struct net_device *dev) +{ + struct bonding *bond = dev->priv; + + list_del(&bond->bond_list); + + bond_destroy_proc_info(bond); +} + +static void bond_free_all(void) +{ + struct bonding *bond, *nxt; + + list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list) { + struct net_device *dev = bond->device; + + unregister_netdev(dev); + bond_deinit(dev); + kfree(dev); + } +} + +/* + * Does not allocate but creates a /proc entry. + * Allowed to fail. + */ static int __init bond_init(struct net_device *dev) { - bonding_t *bond, *this_bond, *last_bond; + struct bonding *bond; int count; + int err = 0; #ifdef BONDING_DEBUG printk (KERN_INFO "Begin bond_init for %s\n", dev->name); #endif - bond = kmalloc(sizeof(struct bonding), GFP_KERNEL); - if (bond == NULL) { - return -ENOMEM; - } - memset(bond, 0, sizeof(struct bonding)); + bond = dev->priv; /* initialize rwlocks */ rwlock_init(&bond->lock); rwlock_init(&bond->ptrlock); + /* Initialize pointers */ bond->next = bond->prev = (slave_t *)bond; bond->current_slave = NULL; bond->current_arp_slave = NULL; bond->device = dev; - dev->priv = bond; /* Initialize the device structure. */ + dev->set_mac_address = bond_set_mac_address; + switch (bond_mode) { case BOND_MODE_ACTIVEBACKUP: dev->hard_start_xmit = bond_xmit_activebackup; @@ -3593,10 +3842,10 @@ case BOND_MODE_TLB: case BOND_MODE_ALB: dev->hard_start_xmit = bond_alb_xmit; + dev->set_mac_address = bond_alb_set_mac_address; break; default: printk(KERN_ERR "Unknown bonding mode %d\n", bond_mode); - kfree(bond); return -EINVAL; } @@ -3605,15 +3854,7 @@ dev->stop = bond_close; dev->set_multicast_list = set_multicast_list; dev->do_ioctl = bond_ioctl; - - /* - * Fill in the fields of the device structure with ethernet-generic - * values. - */ - - ether_setup(dev); - - dev->set_mac_address = bond_set_mac_address; + dev->change_mtu = bond_change_mtu; dev->tx_queue_len = 0; dev->flags |= IFF_MASTER|IFF_MULTICAST; #ifdef CONFIG_NET_FASTROUTE @@ -3641,41 +3882,26 @@ printk("out ARP monitoring\n"); } -#ifdef CONFIG_PROC_FS - bond->bond_proc_dir = proc_mkdir(dev->name, proc_net); - if (bond->bond_proc_dir == NULL) { - printk(KERN_ERR "%s: Cannot init /proc/net/%s/\n", - dev->name, dev->name); - kfree(bond); - return -ENOMEM; - } - bond->bond_proc_info_file = - create_proc_info_entry("info", 0, bond->bond_proc_dir, - bond_get_info); - if (bond->bond_proc_info_file == NULL) { - printk(KERN_ERR "%s: Cannot init /proc/net/%s/info\n", - dev->name, dev->name); - remove_proc_entry(dev->name, proc_net); - kfree(bond); - return -ENOMEM; + err = bond_create_proc_info(bond); + if (err) { + printk(KERN_ERR "%s: Failed to create proc entry\n", + dev->name); + return err; } -#endif /* CONFIG_PROC_FS */ - if (first_pass == 1) { - these_bonds = bond; - register_netdevice_notifier(&bond_netdev_notifier); - first_pass = 0; - } else { - last_bond = these_bonds; - this_bond = these_bonds->next_bond; - while (this_bond != NULL) { - last_bond = this_bond; - this_bond = this_bond->next_bond; - } - last_bond->next_bond = bond; - } + /* Future: + * If anything fails beyond this point + * make sure to destroy the proc entry + */ + + list_add_tail(&bond->bond_list, &bond_dev_list); return 0; +/* +err_out: + bond_destroy_proc_info(bond); + return err; +*/ } /* @@ -3713,9 +3939,6 @@ int no; int err; - /* Find a name for this unit */ - static struct net_device *dev_bond = NULL; - printk(KERN_INFO "%s", version); /* @@ -3731,6 +3954,12 @@ } } + if (USES_PRIMARY(bond_mode)) { + multicast_mode = BOND_MULTICAST_ACTIVE; + } else { + multicast_mode = BOND_MULTICAST_ALL; + } + if (multicast) { multicast_mode = bond_parse_parm(multicast, bond_mc_tbl); if (multicast_mode == -1) { @@ -3766,12 +3995,6 @@ max_bonds, 1, INT_MAX, BOND_DEFAULT_MAX_BONDS); max_bonds = BOND_DEFAULT_MAX_BONDS; } - dev_bond = dev_bonds = kmalloc(max_bonds*sizeof(struct net_device), - GFP_KERNEL); - if (dev_bond == NULL) { - return -ENOMEM; - } - memset(dev_bonds, 0, max_bonds*sizeof(struct net_device)); if (miimon < 0) { printk(KERN_WARNING @@ -3949,9 +4172,7 @@ "link failures! see bonding.txt for details.\n"); } - if ((primary != NULL) && (bond_mode != BOND_MODE_ACTIVEBACKUP) && - (bond_mode != BOND_MODE_TLB) && - (bond_mode != BOND_MODE_ALB)){ + if ((primary != NULL) && !USES_PRIMARY(bond_mode)) { /* currently, using a primary only makes sense * in active backup, TLB or ALB modes */ @@ -3962,47 +4183,62 @@ primary = NULL; } + rtnl_lock(); + err = 0; for (no = 0; no < max_bonds; no++) { - dev_bond->init = bond_init; - - err = dev_alloc_name(dev_bond,"bond%d"); + struct net_device *dev; + + dev = alloc_netdev(sizeof(struct bonding), "", ether_setup); + if (!dev) { + err = -ENOMEM; + goto out_err; + } + + err = dev_alloc_name(dev, "bond%d"); + if (err < 0) { + kfree(dev); + goto out_err; + } + + /* bond_init() must be called after dev_alloc_name() (for the + * /proc files), but before register_netdevice(), because we + * need to set function pointers. + */ + err = bond_init(dev); if (err < 0) { - kfree(dev_bonds); - return err; + kfree(dev); + goto out_err; + } + + SET_MODULE_OWNER(dev); + + err = register_netdevice(dev); + if (err < 0) { + bond_deinit(dev); + kfree(dev); + goto out_err; } - SET_MODULE_OWNER(dev_bond); - if (register_netdev(dev_bond) != 0) { - kfree(dev_bonds); - return -EIO; - } - dev_bond++; } + + rtnl_unlock(); + register_netdevice_notifier(&bond_netdev_notifier); + return 0; + +out_err: + rtnl_unlock(); + + /* free and unregister all bonds that were successfully added */ + bond_free_all(); + + return err; } static void __exit bonding_exit(void) { - struct net_device *dev_bond = dev_bonds; - struct bonding *bond; - int no; - unregister_netdevice_notifier(&bond_netdev_notifier); - - for (no = 0; no < max_bonds; no++) { - -#ifdef CONFIG_PROC_FS - bond = (struct bonding *) dev_bond->priv; - remove_proc_entry("info", bond->bond_proc_dir); - remove_proc_entry(dev_bond->name, proc_net); -#endif - unregister_netdev(dev_bond); - kfree(dev_bond->priv); - - dev_bond->priv = NULL; - dev_bond++; - } - kfree(dev_bonds); + bond_free_all(); } module_init(bonding_init); diff -Nru a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h --- a/drivers/net/bonding/bonding.h Wed Aug 27 18:30:33 2003 +++ b/drivers/net/bonding/bonding.h Wed Aug 27 18:30:33 2003 @@ -103,8 +103,9 @@ #ifdef CONFIG_PROC_FS struct proc_dir_entry *bond_proc_dir; struct proc_dir_entry *bond_proc_info_file; + char procdir_name[IFNAMSIZ]; #endif /* CONFIG_PROC_FS */ - struct bonding *next_bond; + struct list_head bond_list; struct net_device *device; struct dev_mc_list *mc_list; unsigned short flags; diff -Nru a/drivers/net/dmfe.c b/drivers/net/dmfe.c --- a/drivers/net/dmfe.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/dmfe.c Wed Aug 27 18:30:33 2003 @@ -299,7 +299,7 @@ static int dmfe_stop(struct DEVICE *); static struct net_device_stats * dmfe_get_stats(struct DEVICE *); static void dmfe_set_filter_mode(struct DEVICE *); -static int dmfe_do_ioctl(struct DEVICE *, struct ifreq *, int); +static struct ethtool_ops netdev_ethtool_ops; static u16 read_srom_word(long ,int); static void dmfe_interrupt(int , void *, struct pt_regs *); static void dmfe_descriptor_init(struct dmfe_board_info *, unsigned long); @@ -419,7 +419,7 @@ dev->stop = &dmfe_stop; dev->get_stats = &dmfe_get_stats; dev->set_multicast_list = &dmfe_set_filter_mode; - dev->do_ioctl = &dmfe_do_ioctl; + dev->ethtool_ops = &netdev_ethtool_ops; spin_lock_init(&db->lock); pci_read_config_dword(pdev, 0x50, &pci_pmr); @@ -1002,54 +1002,23 @@ } -/* - * Process the ethtool ioctl command - */ - -static int dmfe_ethtool_ioctl(struct net_device *dev, void *useraddr) -{ - struct dmfe_board_info *db = dev->priv; - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: - strcpy(info.driver, DRV_NAME); - strcpy(info.version, DRV_VERSION); - if (db->pdev) - strcpy(info.bus_info, db->pdev->slot_name); - else - sprintf(info.bus_info, "EISA 0x%lx %d", - dev->base_addr, dev->irq); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - - return -EOPNOTSUPP; -} - - -/* - * Process the upper socket ioctl command - */ - -static int dmfe_do_ioctl(struct DEVICE *dev, struct ifreq *ifr, int cmd) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - int retval = -EOPNOTSUPP; - DMFE_DBUG(0, "dmfe_do_ioctl()", 0); - - switch(cmd) { - case SIOCETHTOOL: - return dmfe_ethtool_ioctl(dev, (void*)ifr->ifr_data); - } + struct dmfe_board_info *np = dev->priv; - return retval; + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + if (np->pdev) + strcpy(info->bus_info, pci_name(np->pdev)); + else + sprintf(info->bus_info, "EISA 0x%lx %d", + dev->base_addr, dev->irq); } +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +}; /* * A periodic timer routine diff -Nru a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c --- a/drivers/net/pcmcia/3c574_cs.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/pcmcia/3c574_cs.c Wed Aug 27 18:30:33 2003 @@ -253,6 +253,7 @@ static int el3_close(struct net_device *dev); static void el3_tx_timeout(struct net_device *dev); static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static struct ethtool_ops netdev_ethtool_ops; static void set_rx_mode(struct net_device *dev); static dev_info_t dev_info = "3c574_cs"; @@ -328,6 +329,7 @@ dev->hard_start_xmit = &el3_start_xmit; dev->get_stats = &el3_get_stats; dev->do_ioctl = &el3_ioctl; + SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); dev->set_multicast_list = &set_rx_mode; ether_setup(dev); dev->open = &el3_open; @@ -1191,26 +1193,16 @@ return worklimit; } -static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strncpy(info.driver, "3c574_cs", sizeof(info.driver)-1); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - } - - return -EOPNOTSUPP; + strcpy(info->driver, "3c574_cs"); } +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +}; + /* Provide ioctl() calls to examine the MII xcvr state. */ static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { @@ -1224,8 +1216,6 @@ data[0], data[1], data[2], data[3]); switch(cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, (void *)rq->ifr_data); case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ data[0] = phy; case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ diff -Nru a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c --- a/drivers/net/pcmcia/3c589_cs.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/pcmcia/3c589_cs.c Wed Aug 27 18:30:33 2003 @@ -165,7 +165,7 @@ static int el3_close(struct net_device *dev); static void el3_tx_timeout(struct net_device *dev); static void set_multicast_list(struct net_device *dev); -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); +static struct ethtool_ops netdev_ethtool_ops; static dev_info_t dev_info = "3c589_cs"; @@ -256,7 +256,7 @@ dev->tx_timeout = el3_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; #endif - dev->do_ioctl = netdev_ioctl; + SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); /* Register with Card Services */ link->next = dev_list; @@ -648,70 +648,33 @@ | AdapterFailure, ioaddr + EL3_CMD); } -static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; - - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ - - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - sprintf(info.bus_info, "PCMCIA 0x%lx", dev->base_addr); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + sprintf(info->bus_info, "PCMCIA 0x%lx", dev->base_addr); +} #ifdef PCMCIA_DEBUG - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = pc_debug; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - pc_debug = edata.data; - return 0; - } -#endif - - default: - break; - } - - return -EOPNOTSUPP; +static u32 netdev_get_msglevel(struct net_device *dev) +{ + return pc_debug; } -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) +static void netdev_set_msglevel(struct net_device *dev, u32 level) { - int rc; - - switch (cmd) { - case SIOCETHTOOL: - rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - break; - - default: - rc = -EOPNOTSUPP; - break; - } - - return rc; + pc_debug = level; } +#endif /* PCMCIA_DEBUG */ + +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +#ifdef PCMCIA_DEBUG + .get_msglevel = netdev_get_msglevel, + .set_msglevel = netdev_set_msglevel, +#endif /* PCMCIA_DEBUG */ +}; static int el3_config(struct net_device *dev, struct ifmap *map) { diff -Nru a/drivers/net/pcmcia/aironet4500_cs.c b/drivers/net/pcmcia/aironet4500_cs.c --- a/drivers/net/pcmcia/aironet4500_cs.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/pcmcia/aironet4500_cs.c Wed Aug 27 18:30:33 2003 @@ -165,65 +165,33 @@ return ret; } -static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; - - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ - - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - sprintf(info.bus_info, "PCMCIA 0x%lx", dev->base_addr); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + sprintf(info->bus_info, "PCMCIA 0x%lx", dev->base_addr); +} #ifdef PCMCIA_DEBUG - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = pc_debug; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - pc_debug = edata.data; - return 0; - } -#endif - - default: - break; - } - - return -EOPNOTSUPP; +static u32 netdev_get_msglevel(struct net_device *dev) +{ + return pc_debug; } -static int awc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +static void netdev_set_msglevel(struct net_device *dev, u32 level) { - switch (cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - - default: - return -EOPNOTSUPP; - } - return 0; + pc_debug = level; } +#endif /* PCMCIA_DEBUG */ + +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +#ifdef PCMCIA_DEBUG + .get_msglevel = netdev_get_msglevel, + .set_msglevel = netdev_set_msglevel, +#endif /* PCMCIA_DEBUG */ +}; /* awc_attach() creates an "instance" of the driver, allocating @@ -296,7 +264,7 @@ // dev->set_config = &awc_config_misiganes,aga mitte awc_config; dev->get_stats = &awc_get_stats; // dev->set_multicast_list = &awc_set_multicast_list; - dev->do_ioctl = &awc_ioctl; + SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); strcpy(dev->name, ((struct awc_private *)dev->priv)->node.dev_name); diff -Nru a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c --- a/drivers/net/pcmcia/axnet_cs.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/pcmcia/axnet_cs.c Wed Aug 27 18:30:33 2003 @@ -99,6 +99,8 @@ static int axnet_open(struct net_device *dev); static int axnet_close(struct net_device *dev); static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static struct ethtool_ops netdev_ethtool_ops; + static void ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs); static void ei_watchdog(u_long arg); static void axnet_reset_8390(struct net_device *dev); @@ -221,6 +223,7 @@ dev->open = &axnet_open; dev->stop = &axnet_close; dev->do_ioctl = &axnet_ioctl; + SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); /* Register with Card Services */ link->next = dev_list; @@ -822,26 +825,16 @@ add_timer(&info->watchdog); } -static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strncpy(info.driver, "axnet_cs", sizeof(info.driver)-1); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - } - - return -EOPNOTSUPP; + strcpy(info->driver, "axnet_cs"); } +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +}; + /*====================================================================*/ static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) @@ -850,8 +843,6 @@ u16 *data = (u16 *)&rq->ifr_data; ioaddr_t mii_addr = dev->base_addr + AXNET_MII_EEP; switch (cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); case SIOCDEVPRIVATE: data[0] = info->phy_id; case SIOCDEVPRIVATE+1: diff -Nru a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c --- a/drivers/net/pcmcia/fmvj18x_cs.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/pcmcia/fmvj18x_cs.c Wed Aug 27 18:30:33 2003 @@ -115,7 +115,7 @@ static struct net_device_stats *fjn_get_stats(struct net_device *dev); static void set_rx_mode(struct net_device *dev); static void fjn_tx_timeout(struct net_device *dev); -static int fjn_ioctl(struct net_device *, struct ifreq *, int); +static struct ethtool_ops netdev_ethtool_ops; static dev_info_t dev_info = "fmvj18x_cs"; static dev_link_t *dev_list; @@ -326,7 +326,7 @@ dev->tx_timeout = fjn_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; #endif - dev->do_ioctl = fjn_ioctl; + SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); /* Register with Card Services */ link->next = dev_list; @@ -1205,64 +1205,33 @@ /*====================================================================*/ -static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; - - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ - - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - sprintf(info.bus_info, "PCMCIA 0x%lx", dev->base_addr); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + sprintf(info->bus_info, "PCMCIA 0x%lx", dev->base_addr); +} #ifdef PCMCIA_DEBUG - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = pc_debug; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - pc_debug = edata.data; - return 0; - } -#endif - - default: - break; - } - - return -EOPNOTSUPP; +static u32 netdev_get_msglevel(struct net_device *dev) +{ + return pc_debug; } -static int fjn_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +static void netdev_set_msglevel(struct net_device *dev, u32 level) { - switch (cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - - default: - return -EOPNOTSUPP; - } + pc_debug = level; } +#endif /* PCMCIA_DEBUG */ + +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +#ifdef PCMCIA_DEBUG + .get_msglevel = netdev_get_msglevel, + .set_msglevel = netdev_set_msglevel, +#endif /* PCMCIA_DEBUG */ +}; static int fjn_config(struct net_device *dev, struct ifmap *map){ return 0; diff -Nru a/drivers/net/pcmcia/ibmtr_cs.c b/drivers/net/pcmcia/ibmtr_cs.c --- a/drivers/net/pcmcia/ibmtr_cs.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/pcmcia/ibmtr_cs.c Wed Aug 27 18:30:33 2003 @@ -166,36 +166,15 @@ CardServices(ReportError, handle, &err); } -static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strncpy(info.driver, "ibmtr_cs", sizeof(info.driver)-1); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - } - - return -EOPNOTSUPP; + strcpy(info->driver, "ibmtr_cs"); } -static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - - switch(cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - default: - return -EOPNOTSUPP; - } -} +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +}; /*====================================================================== @@ -249,7 +228,7 @@ link->irq.Instance = info->dev = dev; dev->init = &ibmtr_probe; - dev->do_ioctl = &private_ioctl; + SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); /* Register with Card Services */ link->next = dev_list; diff -Nru a/drivers/net/pcmcia/netwave_cs.c b/drivers/net/pcmcia/netwave_cs.c --- a/drivers/net/pcmcia/netwave_cs.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/pcmcia/netwave_cs.c Wed Aug 27 18:30:33 2003 @@ -247,6 +247,8 @@ #endif static int netwave_ioctl(struct net_device *, struct ifreq *, int); +static struct ethtool_ops netdev_ethtool_ops; + static void set_multicast_list(struct net_device *dev); /* @@ -493,6 +495,7 @@ dev->get_wireless_stats = &netwave_get_wireless_stats; #endif dev->do_ioctl = &netwave_ioctl; + SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); dev->tx_timeout = &netwave_watchdog; dev->watchdog_timeo = TX_TIMEOUT; @@ -603,53 +606,34 @@ } } /* netwave_flush_stale_links */ -static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + sprintf(info->bus_info, "PCMCIA 0x%lx", dev->base_addr); +} - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ +#ifdef PCMCIA_DEBUG +static u32 netdev_get_msglevel(struct net_device *dev) +{ + return pc_debug; +} - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - sprintf(info.bus_info, "PCMCIA 0x%lx", dev->base_addr); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } +static void netdev_set_msglevel(struct net_device *dev, u32 level) +{ + pc_debug = level; +} +#endif /* PCMCIA_DEBUG */ +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, #ifdef PCMCIA_DEBUG - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = pc_debug; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - pc_debug = edata.data; - return 0; - } -#endif + .get_msglevel = netdev_get_msglevel, + .set_msglevel = netdev_set_msglevel, +#endif /* PCMCIA_DEBUG */ +}; - default: - break; - } - - return -EOPNOTSUPP; -} /* * Function netwave_ioctl (dev, rq, cmd) * @@ -672,9 +656,6 @@ DEBUG(0, "%s: ->netwave_ioctl(cmd=0x%X)\n", dev->name, cmd); - if (cmd == SIOCETHTOOL) - return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - /* Disable interrupts & save flags */ save_flags(flags); cli(); diff -Nru a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c --- a/drivers/net/pcmcia/nmclan_cs.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/pcmcia/nmclan_cs.c Wed Aug 27 18:30:33 2003 @@ -438,7 +438,8 @@ static int mace_rx(struct net_device *dev, unsigned char RxCnt); static void restore_multicast_list(struct net_device *dev); static void set_multicast_list(struct net_device *dev); -static int mace_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static struct ethtool_ops netdev_ethtool_ops; + static dev_link_t *nmclan_attach(void); static void nmclan_detach(dev_link_t *); @@ -520,7 +521,7 @@ dev->set_config = &mace_config; dev->get_stats = &mace_get_stats; dev->set_multicast_list = &set_multicast_list; - dev->do_ioctl = &mace_ioctl; + SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); ether_setup(dev); dev->open = &mace_open; dev->stop = &mace_close; @@ -1010,65 +1011,33 @@ return 0; } /* mace_close */ -static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; - - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ - - if (get_user(ethcmd, (u32 *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - sprintf(info.bus_info, "PCMCIA 0x%lx", dev->base_addr); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + sprintf(info->bus_info, "PCMCIA 0x%lx", dev->base_addr); +} #ifdef PCMCIA_DEBUG - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = pc_debug; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - pc_debug = edata.data; - return 0; - } -#endif - - default: - break; - } - - return -EOPNOTSUPP; +static u32 netdev_get_msglevel(struct net_device *dev) +{ + return pc_debug; } -static int mace_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +static void netdev_set_msglevel(struct net_device *dev, u32 level) { - switch (cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - - default: - return -EOPNOTSUPP; - } - return 0; + pc_debug = level; } +#endif /* PCMCIA_DEBUG */ + +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +#ifdef PCMCIA_DEBUG + .get_msglevel = netdev_get_msglevel, + .set_msglevel = netdev_set_msglevel, +#endif /* PCMCIA_DEBUG */ +}; /* ---------------------------------------------------------------------------- mace_start_xmit diff -Nru a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c --- a/drivers/net/pcmcia/pcnet_cs.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/pcmcia/pcnet_cs.c Wed Aug 27 18:30:33 2003 @@ -117,7 +117,9 @@ static int pcnet_open(struct net_device *dev); static int pcnet_close(struct net_device *dev); static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static int do_ioctl_light(struct net_device *dev, struct ifreq *rq, int cmd); + +static struct ethtool_ops netdev_ethtool_ops; + static void ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs); static void ei_watchdog(u_long arg); static void pcnet_reset_8390(struct net_device *dev); @@ -765,6 +767,7 @@ strcpy(info->node.dev_name, dev->name); link->dev = &info->node; + SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); if (info->flags & (IS_DL10019|IS_DL10022)) { u_char id = inb(dev->base_addr + 0x1a); @@ -778,7 +781,6 @@ printk("PNA, "); } else { printk(KERN_INFO "%s: NE2000 Compatible: ", dev->name); - dev->do_ioctl = &do_ioctl_light; } printk("io %#3lx, irq %d,", dev->base_addr, dev->irq); if (info->flags & USE_SHMEM) @@ -1215,26 +1217,16 @@ /*====================================================================*/ -static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strncpy(info.driver, "pcnet_cs", sizeof(info.driver)-1); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - } - - return -EOPNOTSUPP; + strcpy(info->driver, "pcnet_cs"); } +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +}; + /*====================================================================*/ @@ -1244,8 +1236,6 @@ u16 *data = (u16 *)&rq->ifr_data; ioaddr_t mii_addr = dev->base_addr + DLINK_GPIO; switch (cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); case SIOCDEVPRIVATE: data[0] = info->phy_id; case SIOCDEVPRIVATE+1: @@ -1258,17 +1248,6 @@ return 0; } return -EOPNOTSUPP; -} - -/*====================================================================*/ - -static int do_ioctl_light(struct net_device *dev, struct ifreq *rq, int cmd) -{ - switch (cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - } - return -EOPNOTSUPP; } /*====================================================================*/ diff -Nru a/drivers/net/pcmcia/ray_cs.c b/drivers/net/pcmcia/ray_cs.c --- a/drivers/net/pcmcia/ray_cs.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/pcmcia/ray_cs.c Wed Aug 27 18:30:33 2003 @@ -105,6 +105,9 @@ static struct net_device_stats *ray_get_stats(struct net_device *dev); static int ray_dev_init(struct net_device *dev); static int ray_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); + +static struct ethtool_ops netdev_ethtool_ops; + static int ray_open(struct net_device *dev); static int ray_dev_start_xmit(struct sk_buff *skb, struct net_device *dev); static void set_multicast_list(struct net_device *dev); @@ -417,6 +420,7 @@ dev->set_config = &ray_dev_config; dev->get_stats = &ray_get_stats; dev->do_ioctl = &ray_dev_ioctl; + SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); #if WIRELESS_EXT > 7 /* If wireless extension exist in the kernel */ dev->get_wireless_stats = ray_get_wireless_stats; #endif @@ -1236,26 +1240,16 @@ /*===========================================================================*/ -static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strncpy(info.driver, "ray_cs", sizeof(info.driver)-1); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - } - - return -EOPNOTSUPP; + strcpy(info->driver, "ray_cs"); } +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +}; + /*====================================================================*/ static int ray_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) @@ -1275,10 +1269,6 @@ /* Validate the command */ switch (cmd) { - case SIOCETHTOOL: - err = netdev_ethtool_ioctl(dev, (void *) ifr->ifr_data); - break; - #if WIRELESS_EXT > 7 /* --------------- WIRELESS EXTENSIONS --------------- */ /* Get name */ diff -Nru a/drivers/net/pcmcia/wavelan_cs.c b/drivers/net/pcmcia/wavelan_cs.c --- a/drivers/net/pcmcia/wavelan_cs.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/pcmcia/wavelan_cs.c Wed Aug 27 18:30:33 2003 @@ -1890,27 +1890,17 @@ } #endif /* HISTOGRAM */ -static inline int -wl_netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) + +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strncpy(info.driver, "wavelan_cs", sizeof(info.driver)-1); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - } - - return -EOPNOTSUPP; + strcpy(info->driver, "wavelan_cs"); } +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +}; + /*------------------------------------------------------------------*/ /* * Perform ioctl : config & info stuff @@ -1933,9 +1923,6 @@ printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name, cmd); #endif - if (cmd == SIOCETHTOOL) - return wl_netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - /* Disable interrupts & save flags */ wv_splhi(lp, &flags); @@ -4568,6 +4555,7 @@ dev->do_ioctl = wavelan_ioctl; /* wireless extensions */ dev->get_wireless_stats = wavelan_get_wireless_stats; #endif + SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); /* Other specific data */ dev->mtu = WAVELAN_MTU; diff -Nru a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c --- a/drivers/net/pcmcia/xirc2ps_cs.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/pcmcia/xirc2ps_cs.c Wed Aug 27 18:30:33 2003 @@ -386,6 +386,7 @@ static int do_config(struct net_device *dev, struct ifmap *map); static int do_open(struct net_device *dev); static int do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static struct ethtool_ops netdev_ethtool_ops; static void hardreset(struct net_device *dev); static void do_reset(struct net_device *dev, int full); static int init_mii(struct net_device *dev); @@ -650,6 +651,7 @@ dev->set_config = &do_config; dev->get_stats = &do_get_stats; dev->do_ioctl = &do_ioctl; + SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); dev->set_multicast_list = &set_multicast_list; ether_setup(dev); dev->open = &do_open; @@ -1722,26 +1724,16 @@ return 0; } -static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strncpy(info.driver, "xirc2ps_cs", sizeof(info.driver)-1); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - } - - return -EOPNOTSUPP; + strcpy(info->driver, "xirc2ps_cs"); } +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +}; + static int do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { @@ -1757,8 +1749,6 @@ return -EOPNOTSUPP; switch(cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ data[0] = 0; /* we have only this address */ /* fall trough */ diff -Nru a/drivers/net/pcmcia/xircom_cb.c b/drivers/net/pcmcia/xircom_cb.c --- a/drivers/net/pcmcia/xircom_cb.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/pcmcia/xircom_cb.c Wed Aug 27 18:30:33 2003 @@ -176,37 +176,19 @@ } #endif -static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) +static void netdev_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strncpy(info.driver, "xircom_cb", sizeof(info.driver)-1); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - } - - return -EOPNOTSUPP; -} + struct xircom_private *private = dev->priv; -static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - - switch(cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); - default: - return -EOPNOTSUPP; - } + strcpy(info->driver, "xircom_cb"); + strcpy(info->bus_info, pci_name(private->pdev)); } +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, +}; + /* xircom_probe is the code that gets called on device insertion. it sets up the hardware and registers the device to the networklayer. @@ -295,7 +277,7 @@ dev->stop = &xircom_close; dev->get_stats = &xircom_get_stats; dev->priv = private; - dev->do_ioctl = &private_ioctl; + SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); pci_set_drvdata(pdev, dev); /* start the transmitter to get a heartbeat */ diff -Nru a/drivers/net/sis900.c b/drivers/net/sis900.c --- a/drivers/net/sis900.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/sis900.c Wed Aug 27 18:30:33 2003 @@ -98,7 +98,7 @@ "SiS 900 PCI Fast Ethernet", "SiS 7016 PCI Fast Ethernet" }; -static struct pci_device_id sis900_pci_tbl [] __devinitdata = { +static struct pci_device_id sis900_pci_tbl [] = { {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_900}, {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7016, @@ -170,6 +170,7 @@ dma_addr_t rx_ring_dma; unsigned int tx_full; /* The Tx queue is full. */ + u8 host_bridge_rev; }; MODULE_AUTHOR("Jim Huang , Ollie Lho "); @@ -197,7 +198,7 @@ static int sis900_start_xmit(struct sk_buff *skb, struct net_device *net_dev); static int sis900_rx(struct net_device *net_dev); static void sis900_finish_xmit (struct net_device *net_dev); -static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static irqreturn_t sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs); static int sis900_close(struct net_device *net_dev); static int mii_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd); static struct net_device_stats *sis900_get_stats(struct net_device *net_dev); @@ -211,6 +212,7 @@ static u16 sis900_reset_phy(struct net_device *net_dev, int phy_addr); static void sis900_auto_negotiate(struct net_device *net_dev, int phy_addr); static void sis900_set_mode (long ioaddr, int speed, int duplex); +static struct ethtool_ops sis900_ethtool_ops; /** * sis900_get_mac_addr - Get MAC address for stand alone SiS900 model @@ -368,6 +370,7 @@ { struct sis900_private *sis_priv; struct net_device *net_dev; + struct pci_dev *dev; dma_addr_t ring_dma; void *ring_space; long ioaddr; @@ -440,6 +443,7 @@ net_dev->do_ioctl = &mii_ioctl; net_dev->tx_timeout = sis900_tx_timeout; net_dev->watchdog_timeo = TX_TIMEOUT; + net_dev->ethtool_ops = &sis900_ethtool_ops; ret = register_netdev(net_dev); if (ret) @@ -473,6 +477,11 @@ goto err_out_unregister; } + /* save our host bridge revision */ + dev = pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_630, NULL); + if (dev) + pci_read_config_byte(dev, PCI_CLASS_REVISION, &sis_priv->host_bridge_rev); + /* print some information about our NIC */ printk(KERN_INFO "%s: %s at %#lx, IRQ %d, ", net_dev->name, card_name, ioaddr, net_dev->irq); @@ -1108,18 +1117,12 @@ { struct sis900_private *sis_priv = net_dev->priv; u16 reg14h, eq_value=0, max_value=0, min_value=0; - u8 host_bridge_rev; int i, maxcount=10; - struct pci_dev *dev=NULL; if ( !(revision == SIS630E_900_REV || revision == SIS630EA1_900_REV || revision == SIS630A_900_REV || revision == SIS630ET_900_REV) ) return; - dev = pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_630, dev); - if (dev) - pci_read_config_byte(dev, PCI_CLASS_REVISION, &host_bridge_rev); - if (netif_carrier_ok(net_dev)) { reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV); mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (0x2200 | reg14h) & 0xBFFF); @@ -1142,7 +1145,8 @@ } /* 630B0&B1 rule to determine the equalizer value */ if (revision == SIS630A_900_REV && - (host_bridge_rev == SIS630B0 || host_bridge_rev == SIS630B1)) { + (sis_priv->host_bridge_rev == SIS630B0 || + sis_priv->host_bridge_rev == SIS630B1)) { if (max_value == 0) eq_value=3; else @@ -1157,7 +1161,8 @@ else { reg14h=mdio_read(net_dev, sis_priv->cur_phy, MII_RESV); if (revision == SIS630A_900_REV && - (host_bridge_rev == SIS630B0 || host_bridge_rev == SIS630B1)) + (sis_priv->host_bridge_rev == SIS630B0 || + sis_priv->host_bridge_rev == SIS630B1)) mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (reg14h | 0x2200) & 0xBFFF); else mdio_write(net_dev, sis_priv->cur_phy, MII_RESV, (reg14h | 0x2000) & 0xBFFF); @@ -1536,13 +1541,14 @@ * and cleans up after the Tx thread */ -static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +static irqreturn_t sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs) { struct net_device *net_dev = dev_instance; struct sis900_private *sis_priv = net_dev->priv; int boguscnt = max_interrupt_work; long ioaddr = net_dev->base_addr; u32 status; + unsigned int handled = 0; spin_lock (&sis_priv->lock); @@ -1552,6 +1558,7 @@ if ((status & (HIBERR|TxURN|TxERR|TxIDLE|RxORN|RxERR|RxOK)) == 0) /* nothing intresting happened */ break; + handled = 1; /* why dow't we break after Tx/Rx case ?? keyword: full-duplex */ if (status & (RxORN | RxERR | RxOK)) @@ -1582,7 +1589,7 @@ net_dev->name, inl(ioaddr + isr)); spin_unlock (&sis_priv->lock); - return; + return IRQ_RETVAL(handled); } /** @@ -1851,39 +1858,27 @@ } /** - * netdev_ethtool_ioctl - For the basic support of ethtool - * @net_dev: the net device to command for - * @useraddr: start address of interface request + * sis900_get_drvinfo - Return information about driver + * @net_dev: the net device to probe + * @info: container for info returned * * Process ethtool command such as "ehtool -i" to show information */ - -static int netdev_ethtool_ioctl (struct net_device *net_dev, void *useraddr) + +static void sis900_get_drvinfo(struct net_device *net_dev, + struct ethtool_drvinfo *info) { struct sis900_private *sis_priv = net_dev->priv; - u32 ethcmd; - if (copy_from_user (ðcmd, useraddr, sizeof (ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: - { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, SIS900_MODULE_NAME); - strcpy (info.version, SIS900_DRV_VERSION); - strcpy (info.bus_info, sis_priv->pci_dev->slot_name); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } - default: - break; - } - - return -EOPNOTSUPP; + strcpy (info->driver, SIS900_MODULE_NAME); + strcpy (info->version, SIS900_DRV_VERSION); + strcpy (info->bus_info, pci_name(sis_priv->pci_dev)); } +static struct ethtool_ops sis900_ethtool_ops = { + .get_drvinfo = sis900_get_drvinfo, +}; + /** * mii_ioctl - process MII i/o control command * @net_dev: the net device to command for @@ -1899,9 +1894,6 @@ struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data; switch(cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(net_dev, (void *) rq->ifr_data); - case SIOCGMIIPHY: /* Get address of MII PHY in use. */ case SIOCDEVPRIVATE: /* for binary compat, remove in 2.5 */ data->phy_id = sis_priv->mii->phy_addr; diff -Nru a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c --- a/drivers/net/wireless/airo.c Wed Aug 27 18:30:33 2003 +++ b/drivers/net/wireless/airo.c Wed Aug 27 18:30:33 2003 @@ -82,7 +82,7 @@ /* Support Cisco MIC feature */ /* As this feature requires the AES encryption algorithm, it is not included in the kernel tree. If you want to enable it, you need to download the - aes.h, aestab.h and mic.h files from the CVS at + aes.c, aes.h and aestab.h files from the CVS at http://sf.net/projects/airo-linux/ Put the files in the same directory as airo.c and compile normally */ #undef MICSUPPORT @@ -620,14 +620,14 @@ u16 arlDelay; u16 _reserved4[1]; /*---------- Aironet Extensions ----------*/ - u16 magicAction; + u8 magicAction; #define MAGIC_ACTION_STSCHG 1 #define MACIC_ACTION_RESUME 2 #define MAGIC_IGNORE_MCAST (1<<8) #define MAGIC_IGNORE_BCAST (1<<9) #define MAGIC_SWITCH_TO_PSP (0<<10) #define MAGIC_STAY_IN_CAM (1<<10) - u16 magicControl; + u8 magicControl; u16 autoWake; } ConfigRid; @@ -848,6 +848,7 @@ #define AIROGMICRID 11 #define AIROGMICSTATS 12 #define AIROGFLAGS 13 +#define AIRORRID 15 /* Leave gap of 40 commands after AIROGSTATSD32 for future */ @@ -999,6 +1000,8 @@ static int micsetup(struct airo_info *ai); static int encapsulate(struct airo_info *ai, etherHead *pPacket, MICBuffer *buffer, int len); static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *pPacket, u16 payLen); + +#include "aes.c" #endif struct airo_info { @@ -1068,9 +1071,12 @@ #endif /* WIRELESS_SPY */ #endif /* WIRELESS_EXT > 15 */ #endif /* WIRELESS_EXT */ +#ifdef MICSUPPORT /* MIC stuff */ + aes cx; mic_module mod[2]; mic_statistics micstats; +#endif }; static inline int bap_read(struct airo_info *ai, u16 *pu16Dst, int bytelen, @@ -1084,7 +1090,449 @@ struct airo_info *apriv ); #ifdef MICSUPPORT -#include "mic.h" +/*********************************************************************** + * MIC ROUTINES * + *********************************************************************** + */ + +static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq); +static void MoveWindow(miccntx *context, u32 micSeq); +void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, aes *cx); +void emmh32_init(emmh32_context *context); +void emmh32_update(emmh32_context *context, u8 *pOctets, int len); +void emmh32_final(emmh32_context *context, u8 digest[4]); + +/* micinit - Initialize mic seed */ + +static void micinit(struct airo_info *ai) +{ + MICRid mic_rid; + + clear_bit(JOB_MIC, &ai->flags); + PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0); + up(&ai->sem); + + ai->micstats.enabled = (mic_rid.state & 0x00FF) ? 1 : 0; + + if (ai->micstats.enabled) { + /* Key must be valid and different */ + if (mic_rid.multicastValid && (!ai->mod[0].mCtx.valid || + (memcmp (ai->mod[0].mCtx.key, mic_rid.multicast, + sizeof(ai->mod[0].mCtx.key)) != 0))) { + /* Age current mic Context */ + memcpy(&ai->mod[1].mCtx,&ai->mod[0].mCtx,sizeof(miccntx)); + /* Initialize new context */ + memcpy(&ai->mod[0].mCtx.key,mic_rid.multicast,sizeof(mic_rid.multicast)); + ai->mod[0].mCtx.window = 33; //Window always points to the middle + ai->mod[0].mCtx.rx = 0; //Rx Sequence numbers + ai->mod[0].mCtx.tx = 0; //Tx sequence numbers + ai->mod[0].mCtx.valid = 1; //Key is now valid + + /* Give key to mic seed */ + emmh32_setseed(&ai->mod[0].mCtx.seed,mic_rid.multicast,sizeof(mic_rid.multicast), &ai->cx); + } + + /* Key must be valid and different */ + if (mic_rid.unicastValid && (!ai->mod[0].uCtx.valid || + (memcmp(ai->mod[0].uCtx.key, mic_rid.unicast, + sizeof(ai->mod[0].uCtx.key)) != 0))) { + /* Age current mic Context */ + memcpy(&ai->mod[1].uCtx,&ai->mod[0].uCtx,sizeof(miccntx)); + /* Initialize new context */ + memcpy(&ai->mod[0].uCtx.key,mic_rid.unicast,sizeof(mic_rid.unicast)); + + ai->mod[0].uCtx.window = 33; //Window always points to the middle + ai->mod[0].uCtx.rx = 0; //Rx Sequence numbers + ai->mod[0].uCtx.tx = 0; //Tx sequence numbers + ai->mod[0].uCtx.valid = 1; //Key is now valid + + //Give key to mic seed + emmh32_setseed(&ai->mod[0].uCtx.seed, mic_rid.unicast, sizeof(mic_rid.unicast), &ai->cx); + } + } else { + /* So next time we have a valid key and mic is enabled, we will update + * the sequence number if the key is the same as before. + */ + ai->mod[0].uCtx.valid = 0; + ai->mod[0].mCtx.valid = 0; + } +} + +/* micsetup - Get ready for business */ + +static int micsetup(struct airo_info *ai) { + int i; + + for (i=0; i < NUM_MODULES; i++) { + memset(&ai->mod[i].mCtx,0,sizeof(miccntx)); + memset(&ai->mod[i].uCtx,0,sizeof(miccntx)); + } + return SUCCESS; +} + +char micsnap[]= {0xAA,0xAA,0x03,0x00,0x40,0x96,0x00,0x02}; + +/*=========================================================================== + * Description: Mic a packet + * + * Inputs: etherHead * pointer to an 802.3 frame + * + * Returns: BOOLEAN if successful, otherwise false. + * PacketTxLen will be updated with the mic'd packets size. + * + * Caveats: It is assumed that the frame buffer will already + * be big enough to hold the largets mic message possible. + * (No memory allocation is done here). + * + * Author: sbraneky (10/15/01) + * Merciless hacks by rwilcher (1/14/02) + */ + +static int encapsulate(struct airo_info *ai ,etherHead *frame, MICBuffer *mic, int payLen) +{ + miccntx *context; + + // Determine correct context + // If not adhoc, always use unicast key + + if (test_bit(FLAG_ADHOC, &ai->flags) && (frame->da[0] & 0x1)) + context = &ai->mod[0].mCtx; + else + context = &ai->mod[0].uCtx; + + if (!context->valid) + return ERROR; + + mic->typelen = htons(payLen + 16); //Length of Mic'd packet + + memcpy(&mic->u.snap, micsnap, sizeof(micsnap)); // Add Snap + + // Add Tx sequence + mic->seq = htonl(context->tx); + context->tx += 2; + + emmh32_init(&context->seed); // Mic the packet + emmh32_update(&context->seed,frame->da,ETH_ALEN * 2); // DA,SA + emmh32_update(&context->seed,(u8*)&mic->typelen,10); // Type/Length and Snap + emmh32_update(&context->seed,(u8*)&mic->seq,sizeof(mic->seq)); //SEQ + emmh32_update(&context->seed,frame->da + ETH_ALEN * 2,payLen); //payload + emmh32_final(&context->seed, (u8*)&mic->mic); + + /* New Type/length ?????????? */ + mic->typelen = 0; //Let NIC know it could be an oversized packet + return SUCCESS; +} + +typedef enum { + NONE, + NOMIC, + NOMICPLUMMED, + SEQUENCE, + INCORRECTMIC, +} mic_error; + +/*=========================================================================== + * Description: Decapsulates a MIC'd packet and returns the 802.3 packet + * (removes the MIC stuff) if packet is a valid packet. + * + * Inputs: etherHead pointer to the 802.3 packet + * + * Returns: BOOLEAN - TRUE if packet should be dropped otherwise FALSE + * + * Author: sbraneky (10/15/01) + * Merciless hacks by rwilcher (1/14/02) + *--------------------------------------------------------------------------- + */ + +static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *eth, u16 payLen) +{ + int i; + u32 micSEQ; + miccntx *context; + u8 digest[4]; + mic_error micError = NONE; + + // Check if the packet is a Mic'd packet + + if (!ai->micstats.enabled) { + //No Mic set or Mic OFF but we received a MIC'd packet. + if (memcmp ((u8*)eth + 14, micsnap, sizeof(micsnap)) == 0) { + ai->micstats.rxMICPlummed++; + return ERROR; + } + return SUCCESS; + } + + if (ntohs(mic->typelen) == 0x888E) + return SUCCESS; + + if (memcmp (mic->u.snap, micsnap, sizeof(micsnap)) != 0) { + // Mic enabled but packet isn't Mic'd + ai->micstats.rxMICPlummed++; + return ERROR; + } + + micSEQ = ntohl(mic->seq); //store SEQ as CPU order + + //At this point we a have a mic'd packet and mic is enabled + //Now do the mic error checking. + + //Receive seq must be odd + if ( (micSEQ & 1) == 0 ) { + ai->micstats.rxWrongSequence++; + return ERROR; + } + + for (i = 0; i < NUM_MODULES; i++) { + int mcast = eth->da[0] & 1; + //Determine proper context + context = mcast ? &ai->mod[i].mCtx : &ai->mod[i].uCtx; + + //Make sure context is valid + if (!context->valid) { + if (i == 0) + micError = NOMICPLUMMED; + continue; + } + //DeMic it + + if (!mic->typelen) + mic->typelen = htons(payLen + sizeof(MICBuffer) - 2); + + emmh32_init(&context->seed); + emmh32_update(&context->seed, eth->da, ETH_ALEN*2); + emmh32_update(&context->seed, (u8 *)&mic->typelen, sizeof(mic->typelen)+sizeof(mic->u.snap)); + emmh32_update(&context->seed, (u8 *)&mic->seq,sizeof(mic->seq)); + emmh32_update(&context->seed, eth->da + ETH_ALEN*2,payLen); + //Calculate MIC + emmh32_final(&context->seed, digest); + + if (memcmp(digest, &mic->mic, 4)) { //Make sure the mics match + //Invalid Mic + if (i == 0) + micError = INCORRECTMIC; + continue; + } + + //Check Sequence number if mics pass + if (RxSeqValid(ai, context, mcast, micSEQ) == SUCCESS) { + ai->micstats.rxSuccess++; + return SUCCESS; + } + if (i == 0) + micError = SEQUENCE; + } + + // Update statistics + switch (micError) { + case NOMICPLUMMED: ai->micstats.rxMICPlummed++; break; + case SEQUENCE: ai->micstats.rxWrongSequence++; break; + case INCORRECTMIC: ai->micstats.rxIncorrectMIC++; break; + case NONE: break; + case NOMIC: break; + } + return ERROR; +} + +/*=========================================================================== + * Description: Checks the Rx Seq number to make sure it is valid + * and hasn't already been received + * + * Inputs: miccntx - mic context to check seq against + * micSeq - the Mic seq number + * + * Returns: TRUE if valid otherwise FALSE. + * + * Author: sbraneky (10/15/01) + * Merciless hacks by rwilcher (1/14/02) + *--------------------------------------------------------------------------- + */ + +static int RxSeqValid (struct airo_info *ai,miccntx *context,int mcast,u32 micSeq) +{ + u32 seq,index; + + //Allow for the ap being rebooted - if it is then use the next + //sequence number of the current sequence number - might go backwards + + if (mcast) { + if (test_bit(FLAG_UPDATE_MULTI, &ai->flags)) { + clear_bit (FLAG_UPDATE_MULTI, &ai->flags); + context->window = (micSeq > 33) ? micSeq : 33; + context->rx = 0; // Reset rx + } + } else if (test_bit(FLAG_UPDATE_UNI, &ai->flags)) { + clear_bit (FLAG_UPDATE_UNI, &ai->flags); + context->window = (micSeq > 33) ? micSeq : 33; // Move window + context->rx = 0; // Reset rx + } + + //Make sequence number relative to START of window + seq = micSeq - (context->window - 33); + + //Too old of a SEQ number to check. + if ((u32)seq < 0) + return ERROR; + + if ( seq > 64 ) { + //Window is infinite forward + MoveWindow(context,micSeq); + return SUCCESS; + } + + // We are in the window. Now check the context rx bit to see if it was already sent + seq >>= 1; //divide by 2 because we only have odd numbers + index = 1 << seq; //Get an index number + + if (!(context->rx & index)) { + //micSEQ falls inside the window. + //Add seqence number to the list of received numbers. + context->rx |= index; + + MoveWindow(context,micSeq); + + return SUCCESS; + } + return ERROR; +} + +static void MoveWindow(miccntx *context, u32 micSeq) +{ + u32 shift; + + //Move window if seq greater than the middle of the window + if (micSeq > context->window) { + shift = (micSeq - context->window) >> 1; + + //Shift out old + if (shift < 32) + context->rx >>= shift; + else + context->rx = 0; + + context->window = micSeq; //Move window + } +} + +/*==============================================*/ +/*========== EMMH ROUTINES ====================*/ +/*==============================================*/ + +/* mic accumulate */ +#define MIC_ACCUM(val) \ + context->accum += (u64)(val) * context->coeff[coeff_position++]; + +static unsigned char aes_counter[16]; + +/* expand the key to fill the MMH coefficient array */ +void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, aes *cx) +{ + /* take the keying material, expand if necessary, truncate at 16-bytes */ + /* run through AES counter mode to generate context->coeff[] */ + + int i,j; + u32 counter; + u8 cipher[16]; + + set_key(pkey, 16, 1, cx); + counter = 0; + for (i = 0; i < (sizeof(context->coeff)/sizeof(context->coeff[0])); ) { + aes_counter[15] = (u8)(counter >> 0); + aes_counter[14] = (u8)(counter >> 8); + aes_counter[13] = (u8)(counter >> 16); + aes_counter[12] = (u8)(counter >> 24); + counter++; + encrypt(&aes_counter[0], &cipher[0], cx); + for (j=0; (j<16) && (i< (sizeof(context->coeff)/sizeof(context->coeff[0]))); ) { + context->coeff[i++] = ntohl(*(u32 *)&cipher[j]); + j += 4; + } + } +} + +/* prepare for calculation of a new mic */ +void emmh32_init(emmh32_context *context) +{ + /* prepare for new mic calculation */ + context->accum = 0; + context->position = 0; +} + +/* add some bytes to the mic calculation */ +void emmh32_update(emmh32_context *context, u8 *pOctets, int len) +{ + int coeff_position, byte_position; + + if (len == 0) return; + + coeff_position = context->position >> 2; + + /* deal with partial 32-bit word left over from last update */ + byte_position = context->position & 3; + if (byte_position) { + /* have a partial word in part to deal with */ + do { + if (len == 0) return; + context->part.d8[byte_position++] = *pOctets++; + context->position++; + len--; + } while (byte_position < 4); + MIC_ACCUM(htonl(context->part.d32)); + } + + /* deal with full 32-bit words */ + while (len >= 4) { + MIC_ACCUM(htonl(*(u32 *)pOctets)); + context->position += 4; + pOctets += 4; + len -= 4; + } + + /* deal with partial 32-bit word that will be left over from this update */ + byte_position = 0; + while (len > 0) { + context->part.d8[byte_position++] = *pOctets++; + context->position++; + len--; + } +} + +/* mask used to zero empty bytes for final partial word */ +static u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L }; + +/* calculate the mic */ +void emmh32_final(emmh32_context *context, u8 digest[4]) +{ + int coeff_position, byte_position; + u32 val; + + u64 sum, utmp; + s64 stmp; + + coeff_position = context->position >> 2; + + /* deal with partial 32-bit word left over from last update */ + byte_position = context->position & 3; + if (byte_position) { + /* have a partial word in part to deal with */ + val = htonl(context->part.d32); + MIC_ACCUM(val & mask32[byte_position]); /* zero empty bytes */ + } + + /* reduce the accumulated u64 to a 32-bit MIC */ + sum = context->accum; + stmp = (sum & 0xffffffffLL) - ((sum >> 32) * 15); + utmp = (stmp & 0xffffffffLL) - ((stmp >> 32) * 15); + sum = utmp & 0xffffffffLL; + if (utmp > 0x10000000fLL) + sum -= 15; + + val = (u32)sum; + digest[0] = (val>>24) & 0xFF; + digest[1] = (val>>16) & 0xFF; + digest[2] = (val>>8) & 0xFF; + digest[3] = val & 0xFF; +} #endif static int readBSSListRid(struct airo_info *ai, int first, @@ -1561,6 +2009,7 @@ struct sockaddr *addr = p; Resp rsp; + readConfigRid(ai, 1); memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len); ai->need_commit = 1; disable_MAC(ai, 1); @@ -1730,6 +2179,7 @@ ai->thr_pid = kernel_thread(airo_thread, dev, CLONE_FS | CLONE_FILES); if (ai->thr_pid < 0) goto err_out_free; + rc = add_airo_dev( dev ); if (rc) goto err_out_thr; @@ -2133,146 +2583,149 @@ if (len > 2312) { printk( KERN_ERR "airo: Bad size %d\n", len ); - len = 0; + goto badrx; } - if (len) { - if (test_bit(FLAG_802_11, &apriv->flags)) { - bap_read (apriv, (u16*)&fc, sizeof(fc), BAP0); - fc = le16_to_cpu(fc); - switch (fc & 0xc) { - case 4: - if ((fc & 0xe0) == 0xc0) - hdrlen = 10; - else - hdrlen = 16; - break; - case 8: - if ((fc&0x300)==0x300){ - hdrlen = 30; - break; - } - default: - hdrlen = 24; - } - } else - hdrlen = ETH_ALEN * 2; + if (len == 0) + goto badrx; - skb = dev_alloc_skb( len + hdrlen + 2 ); - if ( !skb ) { - apriv->stats.rx_dropped++; - len = 0; - } - } - if (len) { - buffer = (u16*)skb_put (skb, len + hdrlen); - if (test_bit(FLAG_802_11, &apriv->flags)) { - buffer[0] = fc; - bap_read (apriv, buffer + 1, hdrlen - 2, BAP0); - if (hdrlen == 24) - bap_read (apriv, tmpbuf, 6, BAP0); - - bap_read (apriv, &gap, sizeof(gap), BAP0); - gap = le16_to_cpu(gap); - if (gap) { - if (gap <= 8) - bap_read (apriv, tmpbuf, gap, BAP0); + if (test_bit(FLAG_802_11, &apriv->flags)) { + bap_read (apriv, (u16*)&fc, sizeof(fc), BAP0); + fc = le16_to_cpu(fc); + switch (fc & 0xc) { + case 4: + if ((fc & 0xe0) == 0xc0) + hdrlen = 10; else - printk(KERN_ERR "airo: gaplen too big. Problems will follow...\n"); - } - - - bap_read (apriv, buffer + hdrlen/2, len, BAP0); - } else { - MICBuffer micbuf; - bap_read (apriv, buffer, ETH_ALEN*2, BAP0); - if (apriv->micstats.enabled) { - bap_read (apriv,(u16*)&micbuf,sizeof(micbuf),BAP0); - if (ntohs(micbuf.typelen) > 0x05DC) - bap_setup (apriv, fid, 0x44, BAP0); - else { - len -= sizeof(micbuf); - if (len < 48) - len = 48; - skb_trim (skb, len + hdrlen); + hdrlen = 16; + break; + case 8: + if ((fc&0x300)==0x300){ + hdrlen = 30; + break; } - } - bap_read(apriv,buffer+ETH_ALEN,len,BAP0); + default: + hdrlen = 24; + } + } else + hdrlen = ETH_ALEN * 2; + + skb = dev_alloc_skb( len + hdrlen + 2 ); + if ( !skb ) { + apriv->stats.rx_dropped++; + goto badrx; + } + buffer = (u16*)skb_put (skb, len + hdrlen); + if (test_bit(FLAG_802_11, &apriv->flags)) { + buffer[0] = fc; + bap_read (apriv, buffer + 1, hdrlen - 2, BAP0); + if (hdrlen == 24) + bap_read (apriv, tmpbuf, 6, BAP0); + + bap_read (apriv, &gap, sizeof(gap), BAP0); + gap = le16_to_cpu(gap); + if (gap) { + if (gap <= 8) + bap_read (apriv, tmpbuf, gap, BAP0); + else + printk(KERN_ERR "airo: gaplen too big. Problems will follow...\n"); + } + bap_read (apriv, buffer + hdrlen/2, len, BAP0); + } else { #ifdef MICSUPPORT - if (decapsulate(apriv,&micbuf,(etherHead*)buffer,len)) { - dev_kfree_skb_irq (skb); - len = 0; + MICBuffer micbuf; +#endif + bap_read (apriv, buffer, ETH_ALEN*2, BAP0); +#ifdef MICSUPPORT + if (apriv->micstats.enabled) { + bap_read (apriv,(u16*)&micbuf,sizeof(micbuf),BAP0); + if (ntohs(micbuf.typelen) > 0x05DC) + bap_setup (apriv, fid, 0x44, BAP0); + else { + if (len <= sizeof(micbuf)) + goto badmic; + + len -= sizeof(micbuf); + skb_trim (skb, len + hdrlen); } + } #endif + bap_read(apriv,buffer+ETH_ALEN,len,BAP0); +#ifdef MICSUPPORT + if (decapsulate(apriv,&micbuf,(etherHead*)buffer,len)) { +badmic: + dev_kfree_skb_irq (skb); +badrx: + OUT4500( apriv, EVACK, EV_RX); + goto badrx; } +#endif } - if (len) { #if 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 */ - if (!test_bit(FLAG_802_11, &apriv->flags)) { - sa = (char*)buffer + 6; - bap_setup (apriv, fid, 8, BAP0); - bap_read (apriv, (u16*)hdr.rssi, 2, BAP0); - } else - sa = (char*)buffer + 10; - 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); - } + if (apriv->spy_data.spy_number > 0) { + char *sa; + struct iw_quality wstats; + /* Prepare spy data : addr + qual */ + if (!test_bit(FLAG_802_11, &apriv->flags)) { + sa = (char*)buffer + 6; + bap_setup (apriv, fid, 8, BAP0); + bap_read (apriv, (u16*)hdr.rssi, 2, BAP0); + } else + sa = (char*)buffer + 10; + 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 - if (apriv->spy_number > 0) { - int i; - char *sa; - - sa = (char*)buffer + (test_bit(FLAG_802_11, &apriv->flags) ? 10 : 6); - - for (i=0; ispy_number; i++) - if (!memcmp(sa,apriv->spy_address[i],ETH_ALEN)) - { - if (!test_bit(FLAG_802_11, &apriv->flags)) { - bap_setup (apriv, fid, 8, BAP0); - bap_read (apriv, (u16*)hdr.rssi, 2, BAP0); - } - apriv->spy_stat[i].qual = hdr.rssi[0]; - if (apriv->rssi) - apriv->spy_stat[i].level = 0x100 - apriv->rssi[hdr.rssi[1]].rssidBm; - else - apriv->spy_stat[i].level = (hdr.rssi[1] + 321) / 2; - apriv->spy_stat[i].noise = 0; - apriv->spy_stat[i].updated = 3; - break; + if (apriv->spy_number > 0) { + int i; + char *sa; + + sa = (char*)buffer + (test_bit(FLAG_802_11, &apriv->flags) ? 10 : 6); + + for (i=0; ispy_number; i++) + if (!memcmp(sa,apriv->spy_address[i],ETH_ALEN)) + { + if (!test_bit(FLAG_802_11, &apriv->flags)) { + bap_setup (apriv, fid, 8, BAP0); + bap_read (apriv, (u16*)hdr.rssi, 2, BAP0); } - } + apriv->spy_stat[i].qual = hdr.rssi[0]; + if (apriv->rssi) + apriv->spy_stat[i].level = 0x100 - apriv->rssi[hdr.rssi[1]].rssidBm; + else + apriv->spy_stat[i].level = (hdr.rssi[1] + 321) / 2; + apriv->spy_stat[i].noise = 0; + apriv->spy_stat[i].updated = 3; + break; + } + } #endif /* WIRELESS_SPY */ #endif /* WIRELESS_EXT > 15 */ - OUT4500( apriv, EVACK, EV_RX); + OUT4500( apriv, EVACK, EV_RX); - if (test_bit(FLAG_802_11, &apriv->flags)) { - skb->mac.raw = skb->data; - skb->pkt_type = PACKET_OTHERHOST; - skb->dev = apriv->wifidev; - skb->protocol = htons(ETH_P_802_2); - } else { - skb->dev = dev; - skb->protocol = eth_type_trans(skb,dev); - } - skb->dev->last_rx = jiffies; - skb->ip_summed = CHECKSUM_NONE; + if (test_bit(FLAG_802_11, &apriv->flags)) { + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_OTHERHOST; + skb->dev = apriv->wifidev; + skb->protocol = htons(ETH_P_802_2); + } else { + skb->dev = dev; + skb->protocol = eth_type_trans(skb,dev); + } + skb->dev->last_rx = jiffies; + skb->ip_summed = CHECKSUM_NONE; - netif_rx( skb ); - } else - OUT4500( apriv, EVACK, EV_RX); + netif_rx( skb ); } +badrx: /* Check to see if a packet has been transmitted */ if ( status & ( EV_TX|EV_TXEXC ) ) { @@ -2477,6 +2930,9 @@ printk(KERN_WARNING "airo: unknown received signal level scale\n"); } ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS; + ai->config.authType = AUTH_OPEN; + ai->config.modulation = MOD_CCK; + ai->config._reserved1a[0] = 2; /* ??? */ #ifdef MICSUPPORT if ((cap_rid.len>=sizeof(cap_rid)) && (cap_rid.extSoftCap&1) && @@ -2523,6 +2979,7 @@ memcpy(mySsid.ssids[i].ssid, ssids[i], mySsid.ssids[i].len); } + mySsid.len = sizeof(mySsid); } status = writeConfigRid(ai, 1); @@ -3697,6 +4154,8 @@ offset < data->writelen ) offset++; offset++; } + if (i) + SSID_rid.len = sizeof(SSID_rid); disable_MAC(ai, 1); writeSsidRid(ai, &SSID_rid); enable_MAC(ai, &rsp, 1); @@ -4274,6 +4733,7 @@ printk(KERN_DEBUG "%s: New channel value of %d is invalid!\n", dev->name, fwrq->m); rc = -EINVAL; } else { + readConfigRid(local, 1); /* Yes ! We can set it !!! */ local->config.channelSet = (u16)(channel - 1); local->need_commit = 1; @@ -4294,6 +4754,7 @@ struct airo_info *local = dev->priv; StatusRid status_rid; /* Card status info */ + readConfigRid(local, 1); if ((local->config.opmode & 0xFF) == MODE_STA_ESS) status_rid.channel = local->config.channelSet; else @@ -4350,6 +4811,7 @@ sizeof(SSID_rid.ssids[index].ssid)); memcpy(SSID_rid.ssids[index].ssid, extra, dwrq->length); SSID_rid.ssids[index].len = dwrq->length - 1; + SSID_rid.len = sizeof(SSID_rid); } /* Write it to the card */ disable_MAC(local, 1); @@ -4459,6 +4921,7 @@ if(dwrq->length > 16 + 1) { return -E2BIG; } + readConfigRid(local, 1); memset(local->config.nodeName, 0, sizeof(local->config.nodeName)); memcpy(local->config.nodeName, extra, dwrq->length); local->need_commit = 1; @@ -4477,6 +4940,7 @@ { struct airo_info *local = dev->priv; + readConfigRid(local, 1); strncpy(extra, local->config.nodeName, 16); extra[16] = '\0'; dwrq->length = strlen(extra) + 1; @@ -4533,6 +4997,7 @@ return -EINVAL; } + readConfigRid(local, 1); /* Now, check if we want a fixed or auto value */ if(vwrq->fixed == 0) { /* Fill all the rates up to this max rate */ @@ -4569,6 +5034,7 @@ vwrq->value = status_rid.currentXmitRate * 500000; /* If more than one rate, set auto */ + readConfigRid(local, 1); vwrq->fixed = (local->config.rates[1] == 0); return 0; @@ -4591,6 +5057,7 @@ if((rthr < 0) || (rthr > 2312)) { return -EINVAL; } + readConfigRid(local, 1); local->config.rtsThres = rthr; local->need_commit = 1; @@ -4608,6 +5075,7 @@ { struct airo_info *local = dev->priv; + readConfigRid(local, 1); vwrq->value = local->config.rtsThres; vwrq->disabled = (vwrq->value >= 2312); vwrq->fixed = 1; @@ -4633,6 +5101,7 @@ return -EINVAL; } fthr &= ~0x1; /* Get an even value - is it really needed ??? */ + readConfigRid(local, 1); local->config.fragThresh = (u16)fthr; local->need_commit = 1; @@ -4650,6 +5119,7 @@ { struct airo_info *local = dev->priv; + readConfigRid(local, 1); vwrq->value = local->config.fragThresh; vwrq->disabled = (vwrq->value >= 2312); vwrq->fixed = 1; @@ -4669,6 +5139,7 @@ struct airo_info *local = dev->priv; int commit = 1; + readConfigRid(local, 1); if ((local->config.rmode & 0xff) >= RXMODE_RFMON) commit = 2; @@ -4728,6 +5199,7 @@ { struct airo_info *local = dev->priv; + readConfigRid(local, 1); /* If not managed, assume it's ad-hoc */ switch (local->config.opmode & 0xFF) { case MODE_STA_ESS: @@ -4764,6 +5236,7 @@ if(!(cap_rid.softCap & 2)) { return -EOPNOTSUPP; } */ + readConfigRid(local, 1); /* Basic checking: do we have a key to set ? * Note : with the new API, it's impossible to get a NULL pointer. @@ -4850,6 +5323,7 @@ if(!(cap_rid.softCap & 2)) { return -EOPNOTSUPP; } + readConfigRid(local, 1); /* Check encryption mode */ switch(local->config.authType) { case AUTH_ENCRYPT: @@ -4906,6 +5380,7 @@ clear_bit (FLAG_RADIO_OFF, &local->flags); for (i = 0; cap_rid.txPowerLevels[i] && (i < 8); i++) if ((vwrq->value==cap_rid.txPowerLevels[i])) { + readConfigRid(local, 1); local->config.txPower = vwrq->value; local->need_commit = 1; rc = -EINPROGRESS; /* Call commit handler */ @@ -4925,6 +5400,7 @@ { struct airo_info *local = dev->priv; + readConfigRid(local, 1); vwrq->value = local->config.txPower; vwrq->fixed = 1; /* No power control */ vwrq->disabled = test_bit(FLAG_RADIO_OFF, &local->flags); @@ -4948,6 +5424,7 @@ if(vwrq->disabled) { return -EINVAL; } + readConfigRid(local, 1); if(vwrq->flags & IW_RETRY_LIMIT) { if(vwrq->flags & IW_RETRY_MAX) local->config.longRetryLimit = vwrq->value; @@ -4982,6 +5459,7 @@ vwrq->disabled = 0; /* Can't be disabled */ + readConfigRid(local, 1); /* Note : by default, display the min retry number */ if((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { vwrq->flags = IW_RETRY_LIFETIME; @@ -5120,6 +5598,7 @@ { struct airo_info *local = dev->priv; + readConfigRid(local, 1); if (vwrq->disabled) { if ((local->config.rmode & 0xFF) >= RXMODE_RFMON) { return -EINVAL; @@ -5176,6 +5655,7 @@ { struct airo_info *local = dev->priv; + readConfigRid(local, 1); int mode = local->config.powerSaveMode; if ((vwrq->disabled = (mode == POWERSAVE_CAM))) return 0; @@ -5205,6 +5685,7 @@ { struct airo_info *local = dev->priv; + readConfigRid(local, 1); local->config.rssiThreshold = vwrq->disabled ? RSSI_DEFAULT : vwrq->value; local->need_commit = 1; @@ -5222,6 +5703,7 @@ { struct airo_info *local = dev->priv; + readConfigRid(local, 1); vwrq->value = local->config.rssiThreshold; vwrq->disabled = (vwrq->value == 0); vwrq->fixed = 1; @@ -6030,7 +6512,7 @@ /* Separate R/W functions bracket legality here */ - if ( com.command <= AIROGMICSTATS ) + if ( com.command <= AIRORRID ) rc = readrids(dev,&com); else if ( com.command >= AIROPCAP && com.command <= AIROPLEAPUSR ) rc = writerids(dev,&com); @@ -6120,6 +6602,7 @@ static int readrids(struct net_device *dev, aironet_ioctl *comp) { unsigned short ridcode; unsigned char *iobuf; + int len; struct airo_info *ai = dev->priv; if (test_bit(FLAG_FLASHING, &ai->flags)) @@ -6147,11 +6630,14 @@ case AIROGSTAT: ridcode = RID_STATUS; break; case AIROGSTATSD32: ridcode = RID_STATSDELTA; break; case AIROGSTATSC32: ridcode = RID_STATS; break; +#ifdef MICSUPPORT case AIROGMICSTATS: if (copy_to_user(comp->data, &ai->micstats, min((int)comp->len,(int)sizeof(ai->micstats)))) return -EFAULT; return 0; +#endif + case AIRORRID: ridcode = comp->len; break; default: return -EINVAL; break; @@ -6165,9 +6651,12 @@ * then return it to the user * 9/22/2000 Honor user given length */ + if (comp->command == AIRORRID) + len = le16_to_cpu(*(unsigned short *)iobuf); /* Yuck! */ + else + len = comp->len; - if (copy_to_user(comp->data, iobuf, - min((int)comp->len, (int)RIDS_SIZE))) { + if (copy_to_user(comp->data, iobuf, min(len, (int)RIDS_SIZE))) { kfree (iobuf); return -EFAULT; } @@ -6235,9 +6724,11 @@ PC4500_readrid(ai,RID_STATSDELTACLEAR,iobuf,RIDS_SIZE, 1); +#ifdef MICSUPPORT enabled = ai->micstats.enabled; memset(&ai->micstats,0,sizeof(ai->micstats)); ai->micstats.enabled = enabled; +#endif if (copy_to_user(comp->data, iobuf, min((int)comp->len, (int)RIDS_SIZE))) { diff -Nru a/include/linux/list.h b/include/linux/list.h --- a/include/linux/list.h Wed Aug 27 18:30:33 2003 +++ b/include/linux/list.h Wed Aug 27 18:30:33 2003 @@ -227,6 +227,19 @@ pos = list_entry(pos->member.next, typeof(*pos), member), \ prefetch(pos->member.next)) +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + #endif /* __KERNEL__ || _LVM_H_INCLUDE */ #endif diff -Nru a/include/linux/netdevice.h b/include/linux/netdevice.h --- a/include/linux/netdevice.h Wed Aug 27 18:30:33 2003 +++ b/include/linux/netdevice.h Wed Aug 27 18:30:33 2003 @@ -43,6 +43,10 @@ struct vlan_group; struct ethtool_ops; + /* source back-compat hook */ +#define SET_ETHTOOL_OPS(netdev,ops) \ + ( (netdev)->ethtool_ops = (ops) ) + #define HAVE_ALLOC_NETDEV /* feature macro: alloc_xxxdev functions are available. */ diff -Nru a/net/core/ethtool.c b/net/core/ethtool.c --- a/net/core/ethtool.c Wed Aug 27 18:30:33 2003 +++ b/net/core/ethtool.c Wed Aug 27 18:30:33 2003 @@ -503,15 +503,15 @@ switch (gstrings.string_set) { case ETH_SS_TEST: - if (ops->self_test_count) - gstrings.len = ops->self_test_count(dev); - else + if (!ops->self_test_count) return -EOPNOTSUPP; + gstrings.len = ops->self_test_count(dev); + break; case ETH_SS_STATS: - if (ops->get_stats_count) - gstrings.len = ops->get_stats_count(dev); - else + if (!ops->get_stats_count) return -EOPNOTSUPP; + gstrings.len = ops->get_stats_count(dev); + break; default: return -EINVAL; }