diff options
Diffstat (limited to 'drivers/net/wireless/rndis_wlan.c')
-rw-r--r-- | drivers/net/wireless/rndis_wlan.c | 192 |
1 files changed, 167 insertions, 25 deletions
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 4a4f00591447..848cc2cce247 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c | |||
@@ -129,6 +129,7 @@ MODULE_PARM_DESC(workaround_interval, | |||
129 | #define OID_802_11_RTS_THRESHOLD cpu_to_le32(0x0d01020a) | 129 | #define OID_802_11_RTS_THRESHOLD cpu_to_le32(0x0d01020a) |
130 | #define OID_802_11_SUPPORTED_RATES cpu_to_le32(0x0d01020e) | 130 | #define OID_802_11_SUPPORTED_RATES cpu_to_le32(0x0d01020e) |
131 | #define OID_802_11_CONFIGURATION cpu_to_le32(0x0d010211) | 131 | #define OID_802_11_CONFIGURATION cpu_to_le32(0x0d010211) |
132 | #define OID_802_11_POWER_MODE cpu_to_le32(0x0d010216) | ||
132 | #define OID_802_11_BSSID_LIST cpu_to_le32(0x0d010217) | 133 | #define OID_802_11_BSSID_LIST cpu_to_le32(0x0d010217) |
133 | 134 | ||
134 | 135 | ||
@@ -239,6 +240,12 @@ enum ndis_80211_addwep_bits { | |||
239 | NDIS_80211_ADDWEP_TRANSMIT_KEY = cpu_to_le32(1 << 31) | 240 | NDIS_80211_ADDWEP_TRANSMIT_KEY = cpu_to_le32(1 << 31) |
240 | }; | 241 | }; |
241 | 242 | ||
243 | enum ndis_80211_power_mode { | ||
244 | NDIS_80211_POWER_MODE_CAM, | ||
245 | NDIS_80211_POWER_MODE_MAX_PSP, | ||
246 | NDIS_80211_POWER_MODE_FAST_PSP, | ||
247 | }; | ||
248 | |||
242 | struct ndis_80211_auth_request { | 249 | struct ndis_80211_auth_request { |
243 | __le32 length; | 250 | __le32 length; |
244 | u8 bssid[6]; | 251 | u8 bssid[6]; |
@@ -478,6 +485,9 @@ struct rndis_wlan_private { | |||
478 | struct mutex command_lock; | 485 | struct mutex command_lock; |
479 | unsigned long work_pending; | 486 | unsigned long work_pending; |
480 | int last_qual; | 487 | int last_qual; |
488 | s32 cqm_rssi_thold; | ||
489 | u32 cqm_rssi_hyst; | ||
490 | int last_cqm_event_rssi; | ||
481 | 491 | ||
482 | struct ieee80211_supported_band band; | 492 | struct ieee80211_supported_band band; |
483 | struct ieee80211_channel channels[ARRAY_SIZE(rndis_channels)]; | 493 | struct ieee80211_channel channels[ARRAY_SIZE(rndis_channels)]; |
@@ -500,10 +510,10 @@ struct rndis_wlan_private { | |||
500 | 510 | ||
501 | /* hardware state */ | 511 | /* hardware state */ |
502 | bool radio_on; | 512 | bool radio_on; |
513 | int power_mode; | ||
503 | int infra_mode; | 514 | int infra_mode; |
504 | bool connected; | 515 | bool connected; |
505 | u8 bssid[ETH_ALEN]; | 516 | u8 bssid[ETH_ALEN]; |
506 | struct ndis_80211_ssid essid; | ||
507 | __le32 current_command_oid; | 517 | __le32 current_command_oid; |
508 | 518 | ||
509 | /* encryption stuff */ | 519 | /* encryption stuff */ |
@@ -570,7 +580,14 @@ static int rndis_del_pmksa(struct wiphy *wiphy, struct net_device *netdev, | |||
570 | 580 | ||
571 | static int rndis_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev); | 581 | static int rndis_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev); |
572 | 582 | ||
573 | static struct cfg80211_ops rndis_config_ops = { | 583 | static int rndis_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, |
584 | bool enabled, int timeout); | ||
585 | |||
586 | static int rndis_set_cqm_rssi_config(struct wiphy *wiphy, | ||
587 | struct net_device *dev, | ||
588 | s32 rssi_thold, u32 rssi_hyst); | ||
589 | |||
590 | static const struct cfg80211_ops rndis_config_ops = { | ||
574 | .change_virtual_intf = rndis_change_virtual_intf, | 591 | .change_virtual_intf = rndis_change_virtual_intf, |
575 | .scan = rndis_scan, | 592 | .scan = rndis_scan, |
576 | .set_wiphy_params = rndis_set_wiphy_params, | 593 | .set_wiphy_params = rndis_set_wiphy_params, |
@@ -589,6 +606,8 @@ static struct cfg80211_ops rndis_config_ops = { | |||
589 | .set_pmksa = rndis_set_pmksa, | 606 | .set_pmksa = rndis_set_pmksa, |
590 | .del_pmksa = rndis_del_pmksa, | 607 | .del_pmksa = rndis_del_pmksa, |
591 | .flush_pmksa = rndis_flush_pmksa, | 608 | .flush_pmksa = rndis_flush_pmksa, |
609 | .set_power_mgmt = rndis_set_power_mgmt, | ||
610 | .set_cqm_rssi_config = rndis_set_cqm_rssi_config, | ||
592 | }; | 611 | }; |
593 | 612 | ||
594 | static void *rndis_wiphy_privid = &rndis_wiphy_privid; | 613 | static void *rndis_wiphy_privid = &rndis_wiphy_privid; |
@@ -687,6 +706,7 @@ static const char *oid_to_string(__le32 oid) | |||
687 | OID_STR(OID_802_11_ADD_KEY); | 706 | OID_STR(OID_802_11_ADD_KEY); |
688 | OID_STR(OID_802_11_REMOVE_KEY); | 707 | OID_STR(OID_802_11_REMOVE_KEY); |
689 | OID_STR(OID_802_11_ASSOCIATION_INFORMATION); | 708 | OID_STR(OID_802_11_ASSOCIATION_INFORMATION); |
709 | OID_STR(OID_802_11_CAPABILITY); | ||
690 | OID_STR(OID_802_11_PMKID); | 710 | OID_STR(OID_802_11_PMKID); |
691 | OID_STR(OID_802_11_NETWORK_TYPES_SUPPORTED); | 711 | OID_STR(OID_802_11_NETWORK_TYPES_SUPPORTED); |
692 | OID_STR(OID_802_11_NETWORK_TYPE_IN_USE); | 712 | OID_STR(OID_802_11_NETWORK_TYPE_IN_USE); |
@@ -697,6 +717,7 @@ static const char *oid_to_string(__le32 oid) | |||
697 | OID_STR(OID_802_11_RTS_THRESHOLD); | 717 | OID_STR(OID_802_11_RTS_THRESHOLD); |
698 | OID_STR(OID_802_11_SUPPORTED_RATES); | 718 | OID_STR(OID_802_11_SUPPORTED_RATES); |
699 | OID_STR(OID_802_11_CONFIGURATION); | 719 | OID_STR(OID_802_11_CONFIGURATION); |
720 | OID_STR(OID_802_11_POWER_MODE); | ||
700 | OID_STR(OID_802_11_BSSID_LIST); | 721 | OID_STR(OID_802_11_BSSID_LIST); |
701 | #undef OID_STR | 722 | #undef OID_STR |
702 | } | 723 | } |
@@ -1026,7 +1047,6 @@ static int set_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid) | |||
1026 | return ret; | 1047 | return ret; |
1027 | } | 1048 | } |
1028 | if (ret == 0) { | 1049 | if (ret == 0) { |
1029 | memcpy(&priv->essid, ssid, sizeof(priv->essid)); | ||
1030 | priv->radio_on = true; | 1050 | priv->radio_on = true; |
1031 | netdev_dbg(usbdev->net, "%s(): radio_on = true\n", __func__); | 1051 | netdev_dbg(usbdev->net, "%s(): radio_on = true\n", __func__); |
1032 | } | 1052 | } |
@@ -1967,8 +1987,8 @@ static struct cfg80211_bss *rndis_bss_info_update(struct usbnet *usbdev, | |||
1967 | int ie_len, bssid_len; | 1987 | int ie_len, bssid_len; |
1968 | u8 *ie; | 1988 | u8 *ie; |
1969 | 1989 | ||
1970 | netdev_dbg(usbdev->net, " found bssid: '%.32s' [%pM]\n", | 1990 | netdev_dbg(usbdev->net, " found bssid: '%.32s' [%pM], len: %d\n", |
1971 | bssid->ssid.essid, bssid->mac); | 1991 | bssid->ssid.essid, bssid->mac, le32_to_cpu(bssid->length)); |
1972 | 1992 | ||
1973 | /* parse bssid structure */ | 1993 | /* parse bssid structure */ |
1974 | bssid_len = le32_to_cpu(bssid->length); | 1994 | bssid_len = le32_to_cpu(bssid->length); |
@@ -2002,54 +2022,98 @@ static struct cfg80211_bss *rndis_bss_info_update(struct usbnet *usbdev, | |||
2002 | GFP_KERNEL); | 2022 | GFP_KERNEL); |
2003 | } | 2023 | } |
2004 | 2024 | ||
2025 | static struct ndis_80211_bssid_ex *next_bssid_list_item( | ||
2026 | struct ndis_80211_bssid_ex *bssid, | ||
2027 | int *bssid_len, void *buf, int len) | ||
2028 | { | ||
2029 | void *buf_end, *bssid_end; | ||
2030 | |||
2031 | buf_end = (char *)buf + len; | ||
2032 | bssid_end = (char *)bssid + *bssid_len; | ||
2033 | |||
2034 | if ((int)(buf_end - bssid_end) < sizeof(bssid->length)) { | ||
2035 | *bssid_len = 0; | ||
2036 | return NULL; | ||
2037 | } else { | ||
2038 | bssid = (void *)((char *)bssid + *bssid_len); | ||
2039 | *bssid_len = le32_to_cpu(bssid->length); | ||
2040 | return bssid; | ||
2041 | } | ||
2042 | } | ||
2043 | |||
2044 | static bool check_bssid_list_item(struct ndis_80211_bssid_ex *bssid, | ||
2045 | int bssid_len, void *buf, int len) | ||
2046 | { | ||
2047 | void *buf_end, *bssid_end; | ||
2048 | |||
2049 | if (!bssid || bssid_len <= 0 || bssid_len > len) | ||
2050 | return false; | ||
2051 | |||
2052 | buf_end = (char *)buf + len; | ||
2053 | bssid_end = (char *)bssid + bssid_len; | ||
2054 | |||
2055 | return (int)(buf_end - bssid_end) >= 0 && (int)(bssid_end - buf) >= 0; | ||
2056 | } | ||
2057 | |||
2005 | static int rndis_check_bssid_list(struct usbnet *usbdev, u8 *match_bssid, | 2058 | static int rndis_check_bssid_list(struct usbnet *usbdev, u8 *match_bssid, |
2006 | bool *matched) | 2059 | bool *matched) |
2007 | { | 2060 | { |
2008 | void *buf = NULL; | 2061 | void *buf = NULL; |
2009 | struct ndis_80211_bssid_list_ex *bssid_list; | 2062 | struct ndis_80211_bssid_list_ex *bssid_list; |
2010 | struct ndis_80211_bssid_ex *bssid; | 2063 | struct ndis_80211_bssid_ex *bssid; |
2011 | int ret = -EINVAL, len, count, bssid_len; | 2064 | int ret = -EINVAL, len, count, bssid_len, real_count, new_len; |
2012 | bool resized = false; | ||
2013 | 2065 | ||
2014 | netdev_dbg(usbdev->net, "check_bssid_list\n"); | 2066 | netdev_dbg(usbdev->net, "%s()\n", __func__); |
2015 | 2067 | ||
2016 | len = CONTROL_BUFFER_SIZE; | 2068 | len = CONTROL_BUFFER_SIZE; |
2017 | resize_buf: | 2069 | resize_buf: |
2018 | buf = kmalloc(len, GFP_KERNEL); | 2070 | buf = kzalloc(len, GFP_KERNEL); |
2019 | if (!buf) { | 2071 | if (!buf) { |
2020 | ret = -ENOMEM; | 2072 | ret = -ENOMEM; |
2021 | goto out; | 2073 | goto out; |
2022 | } | 2074 | } |
2023 | 2075 | ||
2024 | ret = rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &len); | 2076 | /* BSSID-list might have got bigger last time we checked, keep |
2025 | if (ret != 0) | 2077 | * resizing until it won't get any bigger. |
2078 | */ | ||
2079 | new_len = len; | ||
2080 | ret = rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &new_len); | ||
2081 | if (ret != 0 || new_len < sizeof(struct ndis_80211_bssid_list_ex)) | ||
2026 | goto out; | 2082 | goto out; |
2027 | 2083 | ||
2028 | if (!resized && len > CONTROL_BUFFER_SIZE) { | 2084 | if (new_len > len) { |
2029 | resized = true; | 2085 | len = new_len; |
2030 | kfree(buf); | 2086 | kfree(buf); |
2031 | goto resize_buf; | 2087 | goto resize_buf; |
2032 | } | 2088 | } |
2033 | 2089 | ||
2090 | len = new_len; | ||
2091 | |||
2034 | bssid_list = buf; | 2092 | bssid_list = buf; |
2035 | bssid = bssid_list->bssid; | ||
2036 | bssid_len = le32_to_cpu(bssid->length); | ||
2037 | count = le32_to_cpu(bssid_list->num_items); | 2093 | count = le32_to_cpu(bssid_list->num_items); |
2038 | netdev_dbg(usbdev->net, "check_bssid_list: %d BSSIDs found (buflen: %d)\n", | 2094 | real_count = 0; |
2039 | count, len); | 2095 | netdev_dbg(usbdev->net, "%s(): buflen: %d\n", __func__, len); |
2040 | 2096 | ||
2041 | while (count && ((void *)bssid + bssid_len) <= (buf + len)) { | 2097 | bssid_len = 0; |
2098 | bssid = next_bssid_list_item(bssid_list->bssid, &bssid_len, buf, len); | ||
2099 | |||
2100 | /* Device returns incorrect 'num_items'. Workaround by ignoring the | ||
2101 | * received 'num_items' and walking through full bssid buffer instead. | ||
2102 | */ | ||
2103 | while (check_bssid_list_item(bssid, bssid_len, buf, len)) { | ||
2042 | if (rndis_bss_info_update(usbdev, bssid) && match_bssid && | 2104 | if (rndis_bss_info_update(usbdev, bssid) && match_bssid && |
2043 | matched) { | 2105 | matched) { |
2044 | if (compare_ether_addr(bssid->mac, match_bssid)) | 2106 | if (compare_ether_addr(bssid->mac, match_bssid)) |
2045 | *matched = true; | 2107 | *matched = true; |
2046 | } | 2108 | } |
2047 | 2109 | ||
2048 | bssid = (void *)bssid + bssid_len; | 2110 | real_count++; |
2049 | bssid_len = le32_to_cpu(bssid->length); | 2111 | bssid = next_bssid_list_item(bssid, &bssid_len, buf, len); |
2050 | count--; | ||
2051 | } | 2112 | } |
2052 | 2113 | ||
2114 | netdev_dbg(usbdev->net, "%s(): num_items from device: %d, really found:" | ||
2115 | " %d\n", __func__, count, real_count); | ||
2116 | |||
2053 | out: | 2117 | out: |
2054 | kfree(buf); | 2118 | kfree(buf); |
2055 | return ret; | 2119 | return ret; |
@@ -2391,6 +2455,9 @@ static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev, | |||
2391 | 2455 | ||
2392 | priv->encr_tx_key_index = key_index; | 2456 | priv->encr_tx_key_index = key_index; |
2393 | 2457 | ||
2458 | if (is_wpa_key(priv, key_index)) | ||
2459 | return 0; | ||
2460 | |||
2394 | key = priv->encr_keys[key_index]; | 2461 | key = priv->encr_keys[key_index]; |
2395 | 2462 | ||
2396 | return add_wep_key(usbdev, key.material, key.len, key_index); | 2463 | return add_wep_key(usbdev, key.material, key.len, key_index); |
@@ -2521,6 +2588,51 @@ static int rndis_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev) | |||
2521 | return rndis_set_oid(usbdev, OID_802_11_PMKID, &pmkid, sizeof(pmkid)); | 2588 | return rndis_set_oid(usbdev, OID_802_11_PMKID, &pmkid, sizeof(pmkid)); |
2522 | } | 2589 | } |
2523 | 2590 | ||
2591 | static int rndis_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, | ||
2592 | bool enabled, int timeout) | ||
2593 | { | ||
2594 | struct rndis_wlan_private *priv = wiphy_priv(wiphy); | ||
2595 | struct usbnet *usbdev = priv->usbdev; | ||
2596 | int power_mode; | ||
2597 | __le32 mode; | ||
2598 | int ret; | ||
2599 | |||
2600 | netdev_dbg(usbdev->net, "%s(): %s, %d\n", __func__, | ||
2601 | enabled ? "enabled" : "disabled", | ||
2602 | timeout); | ||
2603 | |||
2604 | if (enabled) | ||
2605 | power_mode = NDIS_80211_POWER_MODE_FAST_PSP; | ||
2606 | else | ||
2607 | power_mode = NDIS_80211_POWER_MODE_CAM; | ||
2608 | |||
2609 | if (power_mode == priv->power_mode) | ||
2610 | return 0; | ||
2611 | |||
2612 | priv->power_mode = power_mode; | ||
2613 | |||
2614 | mode = cpu_to_le32(power_mode); | ||
2615 | ret = rndis_set_oid(usbdev, OID_802_11_POWER_MODE, &mode, sizeof(mode)); | ||
2616 | |||
2617 | netdev_dbg(usbdev->net, "%s(): OID_802_11_POWER_MODE -> %d\n", | ||
2618 | __func__, ret); | ||
2619 | |||
2620 | return ret; | ||
2621 | } | ||
2622 | |||
2623 | static int rndis_set_cqm_rssi_config(struct wiphy *wiphy, | ||
2624 | struct net_device *dev, | ||
2625 | s32 rssi_thold, u32 rssi_hyst) | ||
2626 | { | ||
2627 | struct rndis_wlan_private *priv = wiphy_priv(wiphy); | ||
2628 | |||
2629 | priv->cqm_rssi_thold = rssi_thold; | ||
2630 | priv->cqm_rssi_hyst = rssi_hyst; | ||
2631 | priv->last_cqm_event_rssi = 0; | ||
2632 | |||
2633 | return 0; | ||
2634 | } | ||
2635 | |||
2524 | static void rndis_wlan_craft_connected_bss(struct usbnet *usbdev, u8 *bssid, | 2636 | static void rndis_wlan_craft_connected_bss(struct usbnet *usbdev, u8 *bssid, |
2525 | struct ndis_80211_assoc_info *info) | 2637 | struct ndis_80211_assoc_info *info) |
2526 | { | 2638 | { |
@@ -3050,6 +3162,32 @@ static int rndis_wlan_get_caps(struct usbnet *usbdev, struct wiphy *wiphy) | |||
3050 | return retval; | 3162 | return retval; |
3051 | } | 3163 | } |
3052 | 3164 | ||
3165 | static void rndis_do_cqm(struct usbnet *usbdev, s32 rssi) | ||
3166 | { | ||
3167 | struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); | ||
3168 | enum nl80211_cqm_rssi_threshold_event event; | ||
3169 | int thold, hyst, last_event; | ||
3170 | |||
3171 | if (priv->cqm_rssi_thold >= 0 || rssi >= 0) | ||
3172 | return; | ||
3173 | if (priv->infra_mode != NDIS_80211_INFRA_INFRA) | ||
3174 | return; | ||
3175 | |||
3176 | last_event = priv->last_cqm_event_rssi; | ||
3177 | thold = priv->cqm_rssi_thold; | ||
3178 | hyst = priv->cqm_rssi_hyst; | ||
3179 | |||
3180 | if (rssi < thold && (last_event == 0 || rssi < last_event - hyst)) | ||
3181 | event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; | ||
3182 | else if (rssi > thold && (last_event == 0 || rssi > last_event + hyst)) | ||
3183 | event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; | ||
3184 | else | ||
3185 | return; | ||
3186 | |||
3187 | priv->last_cqm_event_rssi = rssi; | ||
3188 | cfg80211_cqm_rssi_notify(usbdev->net, event, GFP_KERNEL); | ||
3189 | } | ||
3190 | |||
3053 | #define DEVICE_POLLER_JIFFIES (HZ) | 3191 | #define DEVICE_POLLER_JIFFIES (HZ) |
3054 | static void rndis_device_poller(struct work_struct *work) | 3192 | static void rndis_device_poller(struct work_struct *work) |
3055 | { | 3193 | { |
@@ -3084,8 +3222,10 @@ static void rndis_device_poller(struct work_struct *work) | |||
3084 | 3222 | ||
3085 | len = sizeof(rssi); | 3223 | len = sizeof(rssi); |
3086 | ret = rndis_query_oid(usbdev, OID_802_11_RSSI, &rssi, &len); | 3224 | ret = rndis_query_oid(usbdev, OID_802_11_RSSI, &rssi, &len); |
3087 | if (ret == 0) | 3225 | if (ret == 0) { |
3088 | priv->last_qual = level_to_qual(le32_to_cpu(rssi)); | 3226 | priv->last_qual = level_to_qual(le32_to_cpu(rssi)); |
3227 | rndis_do_cqm(usbdev, le32_to_cpu(rssi)); | ||
3228 | } | ||
3089 | 3229 | ||
3090 | netdev_dbg(usbdev->net, "dev-poller: OID_802_11_RSSI -> %d, rssi:%d, qual: %d\n", | 3230 | netdev_dbg(usbdev->net, "dev-poller: OID_802_11_RSSI -> %d, rssi:%d, qual: %d\n", |
3091 | ret, le32_to_cpu(rssi), level_to_qual(le32_to_cpu(rssi))); | 3231 | ret, le32_to_cpu(rssi), level_to_qual(le32_to_cpu(rssi))); |
@@ -3347,13 +3487,15 @@ static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf) | |||
3347 | 3487 | ||
3348 | set_default_iw_params(usbdev); | 3488 | set_default_iw_params(usbdev); |
3349 | 3489 | ||
3490 | priv->power_mode = -1; | ||
3491 | |||
3350 | /* set default rts/frag */ | 3492 | /* set default rts/frag */ |
3351 | rndis_set_wiphy_params(wiphy, | 3493 | rndis_set_wiphy_params(wiphy, |
3352 | WIPHY_PARAM_FRAG_THRESHOLD | WIPHY_PARAM_RTS_THRESHOLD); | 3494 | WIPHY_PARAM_FRAG_THRESHOLD | WIPHY_PARAM_RTS_THRESHOLD); |
3353 | 3495 | ||
3354 | /* turn radio on */ | 3496 | /* turn radio off on init */ |
3355 | priv->radio_on = true; | 3497 | priv->radio_on = false; |
3356 | disassociate(usbdev, true); | 3498 | disassociate(usbdev, false); |
3357 | netif_carrier_off(usbdev->net); | 3499 | netif_carrier_off(usbdev->net); |
3358 | 3500 | ||
3359 | return 0; | 3501 | return 0; |