diff options
Diffstat (limited to 'drivers/net/wireless/rndis_wlan.c')
-rw-r--r-- | drivers/net/wireless/rndis_wlan.c | 298 |
1 files changed, 143 insertions, 155 deletions
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 3bec3dbd3450..09c0702ae645 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -139,9 +139,15 @@ MODULE_PARM_DESC(workaround_interval, /* Assume that Broadcom 4320 (only chipset at time of writing known to be * based on wireless rndis) has default txpower of 13dBm. * This value is from Linksys WUSB54GSC User Guide, Appendix F: Specifications. - * 13dBm == 19.9mW + * 100% : 20 mW ~ 13dBm + * 75% : 15 mW ~ 12dBm + * 50% : 10 mW ~ 10dBm + * 25% : 5 mW ~ 7dBm */ -#define BCM4320_DEFAULT_TXPOWER 20 +#define BCM4320_DEFAULT_TXPOWER_DBM_100 13 +#define BCM4320_DEFAULT_TXPOWER_DBM_75 12 +#define BCM4320_DEFAULT_TXPOWER_DBM_50 10 +#define BCM4320_DEFAULT_TXPOWER_DBM_25 7 /* codes for "status" field of completion messages */ @@ -420,21 +426,30 @@ struct rndis_wlan_private { /* * cfg80211 ops */ -static int rndis_change_virtual_intf(struct wiphy *wiphy, int ifindex, +static int rndis_change_virtual_intf(struct wiphy *wiphy, + struct net_device *dev, enum nl80211_iftype type, u32 *flags, struct vif_params *params); static int rndis_scan(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_scan_request *request); +static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed); + +static int rndis_set_tx_power(struct wiphy *wiphy, enum tx_power_setting type, + int dbm); +static int rndis_get_tx_power(struct wiphy *wiphy, int *dbm); + static struct cfg80211_ops rndis_config_ops = { .change_virtual_intf = rndis_change_virtual_intf, .scan = rndis_scan, + .set_wiphy_params = rndis_set_wiphy_params, + .set_tx_power = rndis_set_tx_power, + .get_tx_power = rndis_get_tx_power, }; static void *rndis_wiphy_privid = &rndis_wiphy_privid; -static const int bcm4320_power_output[4] = { 25, 50, 75, 100 }; static const unsigned char zero_bssid[ETH_ALEN] = {0,}; static const unsigned char ffff_bssid[ETH_ALEN] = { 0xff, 0xff, 0xff, @@ -447,10 +462,19 @@ static struct rndis_wlan_private *get_rndis_wlan_priv(struct usbnet *dev) } -static u32 get_bcm4320_power(struct rndis_wlan_private *priv) +static u32 get_bcm4320_power_dbm(struct rndis_wlan_private *priv) { - return BCM4320_DEFAULT_TXPOWER * - bcm4320_power_output[priv->param_power_output] / 100; + switch (priv->param_power_output) { + default: + case 3: + return BCM4320_DEFAULT_TXPOWER_DBM_100; + case 2: + return BCM4320_DEFAULT_TXPOWER_DBM_75; + case 1: + return BCM4320_DEFAULT_TXPOWER_DBM_50; + case 0: + return BCM4320_DEFAULT_TXPOWER_DBM_25; + } } @@ -968,6 +992,36 @@ static int set_infra_mode(struct usbnet *usbdev, int mode) } +static int set_rts_threshold(struct usbnet *usbdev, u32 rts_threshold) +{ + __le32 tmp; + + devdbg(usbdev, "set_rts_threshold %i", rts_threshold); + + if (rts_threshold < 0 || rts_threshold > 2347) + rts_threshold = 2347; + + tmp = cpu_to_le32(rts_threshold); + return rndis_set_oid(usbdev, OID_802_11_RTS_THRESHOLD, &tmp, + sizeof(tmp)); +} + + +static int set_frag_threshold(struct usbnet *usbdev, u32 frag_threshold) +{ + __le32 tmp; + + devdbg(usbdev, "set_frag_threshold %i", frag_threshold); + + if (frag_threshold < 256 || frag_threshold > 2346) + frag_threshold = 2346; + + tmp = cpu_to_le32(frag_threshold); + return rndis_set_oid(usbdev, OID_802_11_FRAGMENTATION_THRESHOLD, &tmp, + sizeof(tmp)); +} + + static void set_default_iw_params(struct usbnet *usbdev) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); @@ -1222,20 +1276,14 @@ static void set_multicast_list(struct usbnet *usbdev) /* * cfg80211 ops */ -static int rndis_change_virtual_intf(struct wiphy *wiphy, int ifindex, +static int rndis_change_virtual_intf(struct wiphy *wiphy, + struct net_device *dev, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { - struct net_device *dev; - struct usbnet *usbdev; + struct usbnet *usbdev = netdev_priv(dev); int mode; - /* we're under RTNL */ - dev = __dev_get_by_index(&init_net, ifindex); - if (!dev) - return -ENODEV; - usbdev = netdev_priv(dev); - switch (type) { case NL80211_IFTYPE_ADHOC: mode = NDIS_80211_INFRA_ADHOC; @@ -1251,6 +1299,64 @@ static int rndis_change_virtual_intf(struct wiphy *wiphy, int ifindex, } +static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + int err; + + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { + err = set_frag_threshold(usbdev, wiphy->frag_threshold); + if (err < 0) + return err; + } + + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { + err = set_rts_threshold(usbdev, wiphy->rts_threshold); + if (err < 0) + return err; + } + + return 0; +} + + +static int rndis_set_tx_power(struct wiphy *wiphy, enum tx_power_setting type, + int dbm) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + + devdbg(usbdev, "rndis_set_tx_power type:0x%x dbm:%i", type, dbm); + + /* Device doesn't support changing txpower after initialization, only + * turn off/on radio. Support 'auto' mode and setting same dBm that is + * currently used. + */ + if (type == TX_POWER_AUTOMATIC || dbm == get_bcm4320_power_dbm(priv)) { + if (!priv->radio_on) + disassociate(usbdev, 1); /* turn on radio */ + + return 0; + } + + return -ENOTSUPP; +} + + +static int rndis_get_tx_power(struct wiphy *wiphy, int *dbm) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + + *dbm = get_bcm4320_power_dbm(priv); + + devdbg(usbdev, "rndis_get_tx_power dbm:%i", *dbm); + + return 0; +} + + #define SCAN_DELAY_JIFFIES (HZ) static int rndis_scan(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_scan_request *request) @@ -1766,74 +1872,6 @@ static int rndis_iw_get_genie(struct net_device *dev, } -static int rndis_iw_set_rts(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -{ - struct usbnet *usbdev = netdev_priv(dev); - __le32 tmp; - devdbg(usbdev, "SIOCSIWRTS"); - - tmp = cpu_to_le32(wrqu->rts.value); - return rndis_set_oid(usbdev, OID_802_11_RTS_THRESHOLD, &tmp, - sizeof(tmp)); -} - - -static int rndis_iw_get_rts(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -{ - struct usbnet *usbdev = netdev_priv(dev); - __le32 tmp; - int len, ret; - - len = sizeof(tmp); - ret = rndis_query_oid(usbdev, OID_802_11_RTS_THRESHOLD, &tmp, &len); - if (ret == 0) { - wrqu->rts.value = le32_to_cpu(tmp); - wrqu->rts.flags = 1; - wrqu->rts.disabled = 0; - } - - devdbg(usbdev, "SIOCGIWRTS: %d", wrqu->rts.value); - - return ret; -} - - -static int rndis_iw_set_frag(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -{ - struct usbnet *usbdev = netdev_priv(dev); - __le32 tmp; - - devdbg(usbdev, "SIOCSIWFRAG"); - - tmp = cpu_to_le32(wrqu->frag.value); - return rndis_set_oid(usbdev, OID_802_11_FRAGMENTATION_THRESHOLD, &tmp, - sizeof(tmp)); -} - - -static int rndis_iw_get_frag(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -{ - struct usbnet *usbdev = netdev_priv(dev); - __le32 tmp; - int len, ret; - - len = sizeof(tmp); - ret = rndis_query_oid(usbdev, OID_802_11_FRAGMENTATION_THRESHOLD, &tmp, - &len); - if (ret == 0) { - wrqu->frag.value = le32_to_cpu(tmp); - wrqu->frag.flags = 1; - wrqu->frag.disabled = 0; - } - devdbg(usbdev, "SIOCGIWFRAG: %d", wrqu->frag.value); - return ret; -} - - static int rndis_iw_set_freq(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { @@ -1882,71 +1920,6 @@ static int rndis_iw_get_freq(struct net_device *dev, } -static int rndis_iw_get_txpower(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -{ - struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - __le32 tx_power; - - if (priv->radio_on) { - /* fake since changing tx_power (by userlevel) not supported */ - tx_power = cpu_to_le32(get_bcm4320_power(priv)); - - wrqu->txpower.flags = IW_TXPOW_MWATT; - wrqu->txpower.value = le32_to_cpu(tx_power); - wrqu->txpower.disabled = 0; - } else { - wrqu->txpower.flags = IW_TXPOW_MWATT; - wrqu->txpower.value = 0; - wrqu->txpower.disabled = 1; - } - - devdbg(usbdev, "SIOCGIWTXPOW: %d", wrqu->txpower.value); - - return 0; -} - - -static int rndis_iw_set_txpower(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -{ - struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - __le32 tx_power = 0; - - if (!wrqu->txpower.disabled) { - if (wrqu->txpower.flags == IW_TXPOW_MWATT) - tx_power = cpu_to_le32(wrqu->txpower.value); - else { /* wrqu->txpower.flags == IW_TXPOW_DBM */ - if (wrqu->txpower.value > 20) - tx_power = cpu_to_le32(128); - else if (wrqu->txpower.value < -43) - tx_power = cpu_to_le32(127); - else { - signed char tmp; - tmp = wrqu->txpower.value; - tmp = -12 - tmp; - tmp <<= 2; - tx_power = cpu_to_le32((unsigned char)tmp); - } - } - } - - devdbg(usbdev, "SIOCSIWTXPOW: %d", le32_to_cpu(tx_power)); - - if (le32_to_cpu(tx_power) != 0) { - /* txpower unsupported, just turn radio on */ - if (!priv->radio_on) - return disassociate(usbdev, 1); - return 0; /* all ready on */ - } - - /* tx_power == 0, turn off radio */ - return disassociate(usbdev, 0); -} - - static int rndis_iw_get_rate(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { @@ -2022,12 +1995,12 @@ static const iw_handler rndis_iw_handler[] = IW_IOCTL(SIOCSIWESSID) = rndis_iw_set_essid, IW_IOCTL(SIOCGIWESSID) = rndis_iw_get_essid, IW_IOCTL(SIOCGIWRATE) = rndis_iw_get_rate, - IW_IOCTL(SIOCSIWRTS) = rndis_iw_set_rts, - IW_IOCTL(SIOCGIWRTS) = rndis_iw_get_rts, - IW_IOCTL(SIOCSIWFRAG) = rndis_iw_set_frag, - IW_IOCTL(SIOCGIWFRAG) = rndis_iw_get_frag, - IW_IOCTL(SIOCSIWTXPOW) = rndis_iw_set_txpower, - IW_IOCTL(SIOCGIWTXPOW) = rndis_iw_get_txpower, + IW_IOCTL(SIOCSIWRTS) = (iw_handler) cfg80211_wext_siwrts, + IW_IOCTL(SIOCGIWRTS) = (iw_handler) cfg80211_wext_giwrts, + IW_IOCTL(SIOCSIWFRAG) = (iw_handler) cfg80211_wext_siwfrag, + IW_IOCTL(SIOCGIWFRAG) = (iw_handler) cfg80211_wext_giwfrag, + IW_IOCTL(SIOCSIWTXPOW) = (iw_handler) cfg80211_wext_siwtxpower, + IW_IOCTL(SIOCGIWTXPOW) = (iw_handler) cfg80211_wext_giwtxpower, IW_IOCTL(SIOCSIWENCODE) = rndis_iw_set_encode, IW_IOCTL(SIOCSIWENCODEEXT) = rndis_iw_set_encode_ext, IW_IOCTL(SIOCSIWAUTH) = rndis_iw_set_auth, @@ -2475,6 +2448,10 @@ static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf) set_default_iw_params(usbdev); + /* set default rts/frag */ + rndis_set_wiphy_params(wiphy, + WIPHY_PARAM_FRAG_THRESHOLD | WIPHY_PARAM_RTS_THRESHOLD); + /* turn radio on */ priv->radio_on = 1; disassociate(usbdev, 1); @@ -2522,10 +2499,18 @@ static void rndis_wlan_unbind(struct usbnet *usbdev, struct usb_interface *intf) static int rndis_wlan_reset(struct usbnet *usbdev) { + devdbg(usbdev, "rndis_wlan_reset"); return deauthenticate(usbdev); } +static int rndis_wlan_stop(struct usbnet *usbdev) +{ + devdbg(usbdev, "rndis_wlan_stop"); + return disassociate(usbdev, 0); +} + + static const struct driver_info bcm4320b_info = { .description = "Wireless RNDIS device, BCM4320b based", .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT, @@ -2535,6 +2520,7 @@ static const struct driver_info bcm4320b_info = { .rx_fixup = rndis_rx_fixup, .tx_fixup = rndis_tx_fixup, .reset = rndis_wlan_reset, + .stop = rndis_wlan_stop, .early_init = bcm4320b_early_init, .link_change = rndis_wlan_link_change, }; @@ -2548,6 +2534,7 @@ static const struct driver_info bcm4320a_info = { .rx_fixup = rndis_rx_fixup, .tx_fixup = rndis_tx_fixup, .reset = rndis_wlan_reset, + .stop = rndis_wlan_stop, .early_init = bcm4320a_early_init, .link_change = rndis_wlan_link_change, }; @@ -2561,6 +2548,7 @@ static const struct driver_info rndis_wlan_info = { .rx_fixup = rndis_rx_fixup, .tx_fixup = rndis_tx_fixup, .reset = rndis_wlan_reset, + .stop = rndis_wlan_stop, .early_init = bcm4320a_early_init, .link_change = rndis_wlan_link_change, }; |