aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJussi Kivilinna <jussi.kivilinna@mbnet.fi>2010-11-09 12:25:47 -0500
committerJohn W. Linville <linville@tuxdriver.com>2010-11-16 16:37:04 -0500
commitb5257c952dda24df7078c74b7b811b44c6e49206 (patch)
treec6d3f9c13344d1ed74aedec0b8a59e4e7716ad23
parentf23a478075659db8a4fd62fa6e264a8bb052cc5b (diff)
rndis_wlan: workaround device not returning bss for currently connected AP
BCM4320a devices do not return bss for currently connected AP in bss-list, althought this is required by NDIS specs. Missing bss leads to warning at net/wireless/sme.c:__cfg80211_connect_result(), WARN_ON(!bss). Workaround this by crafting bss manually with information we can read from device. Workaround is only used when device bss-list does not return current bss, and so is only used with BCM4320a devices and not newer BCM4320b ones. Fixes bug #20152. Reported-by: Luís Picciochi <Pitxyoki@gmail.com> Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/rndis_wlan.c130
1 files changed, 121 insertions, 9 deletions
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 71b5971da597..0a423c49aab9 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -994,7 +994,8 @@ static int level_to_qual(int level)
994 */ 994 */
995static int set_infra_mode(struct usbnet *usbdev, int mode); 995static int set_infra_mode(struct usbnet *usbdev, int mode);
996static void restore_keys(struct usbnet *usbdev); 996static void restore_keys(struct usbnet *usbdev);
997static int rndis_check_bssid_list(struct usbnet *usbdev); 997static int rndis_check_bssid_list(struct usbnet *usbdev, u8 *match_bssid,
998 bool *matched);
998 999
999static int set_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid) 1000static int set_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid)
1000{ 1001{
@@ -1911,7 +1912,7 @@ static int rndis_scan(struct wiphy *wiphy, struct net_device *dev,
1911 /* Get current bssid list from device before new scan, as new scan 1912 /* Get current bssid list from device before new scan, as new scan
1912 * clears internal bssid list. 1913 * clears internal bssid list.
1913 */ 1914 */
1914 rndis_check_bssid_list(usbdev); 1915 rndis_check_bssid_list(usbdev, NULL, NULL);
1915 1916
1916 if (!request) 1917 if (!request)
1917 return -EINVAL; 1918 return -EINVAL;
@@ -1981,7 +1982,8 @@ static struct cfg80211_bss *rndis_bss_info_update(struct usbnet *usbdev,
1981 GFP_KERNEL); 1982 GFP_KERNEL);
1982} 1983}
1983 1984
1984static int rndis_check_bssid_list(struct usbnet *usbdev) 1985static int rndis_check_bssid_list(struct usbnet *usbdev, u8 *match_bssid,
1986 bool *matched)
1985{ 1987{
1986 void *buf = NULL; 1988 void *buf = NULL;
1987 struct ndis_80211_bssid_list_ex *bssid_list; 1989 struct ndis_80211_bssid_list_ex *bssid_list;
@@ -2017,7 +2019,11 @@ resize_buf:
2017 count, len); 2019 count, len);
2018 2020
2019 while (count && ((void *)bssid + bssid_len) <= (buf + len)) { 2021 while (count && ((void *)bssid + bssid_len) <= (buf + len)) {
2020 rndis_bss_info_update(usbdev, bssid); 2022 if (rndis_bss_info_update(usbdev, bssid) && match_bssid &&
2023 matched) {
2024 if (compare_ether_addr(bssid->mac, match_bssid))
2025 *matched = true;
2026 }
2021 2027
2022 bssid = (void *)bssid + bssid_len; 2028 bssid = (void *)bssid + bssid_len;
2023 bssid_len = le32_to_cpu(bssid->length); 2029 bssid_len = le32_to_cpu(bssid->length);
@@ -2041,7 +2047,7 @@ static void rndis_get_scan_results(struct work_struct *work)
2041 if (!priv->scan_request) 2047 if (!priv->scan_request)
2042 return; 2048 return;
2043 2049
2044 ret = rndis_check_bssid_list(usbdev); 2050 ret = rndis_check_bssid_list(usbdev, NULL, NULL);
2045 2051
2046 cfg80211_scan_done(priv->scan_request, ret < 0); 2052 cfg80211_scan_done(priv->scan_request, ret < 0);
2047 2053
@@ -2495,6 +2501,91 @@ static int rndis_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
2495 return rndis_set_oid(usbdev, OID_802_11_PMKID, &pmkid, sizeof(pmkid)); 2501 return rndis_set_oid(usbdev, OID_802_11_PMKID, &pmkid, sizeof(pmkid));
2496} 2502}
2497 2503
2504static void rndis_wlan_craft_connected_bss(struct usbnet *usbdev, u8 *bssid,
2505 struct ndis_80211_assoc_info *info)
2506{
2507 struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev);
2508 struct ieee80211_channel *channel;
2509 struct ndis_80211_conf config;
2510 struct ndis_80211_ssid ssid;
2511 s32 signal;
2512 u64 timestamp;
2513 u16 capability;
2514 u16 beacon_interval;
2515 __le32 rssi;
2516 u8 ie_buf[34];
2517 int len, ret, ie_len;
2518
2519 /* Get signal quality, in case of error use rssi=0 and ignore error. */
2520 len = sizeof(rssi);
2521 rssi = 0;
2522 rndis_query_oid(usbdev, OID_802_11_RSSI, &rssi, &len);
2523 signal = level_to_qual(le32_to_cpu(rssi));
2524
2525 netdev_dbg(usbdev->net, "%s(): OID_802_11_RSSI -> %d, "
2526 "rssi:%d, qual: %d\n", __func__, ret, le32_to_cpu(rssi),
2527 level_to_qual(le32_to_cpu(rssi)));
2528
2529 /* Get AP capabilities */
2530 if (info) {
2531 capability = le16_to_cpu(info->resp_ie.capa);
2532 } else {
2533 /* Set atleast ESS/IBSS capability */
2534 capability = (priv->infra_mode == NDIS_80211_INFRA_INFRA) ?
2535 WLAN_CAPABILITY_ESS : WLAN_CAPABILITY_IBSS;
2536 }
2537
2538 /* Get channel and beacon interval */
2539 len = sizeof(config);
2540 ret = rndis_query_oid(usbdev, OID_802_11_CONFIGURATION, &config, &len);
2541 netdev_dbg(usbdev->net, "%s(): OID_802_11_CONFIGURATION -> %d\n",
2542 __func__, ret);
2543 if (ret >= 0) {
2544 beacon_interval = le16_to_cpu(config.beacon_period);
2545 channel = ieee80211_get_channel(priv->wdev.wiphy,
2546 KHZ_TO_MHZ(le32_to_cpu(config.ds_config)));
2547 if (!channel) {
2548 netdev_warn(usbdev->net, "%s(): could not get channel."
2549 "\n", __func__);
2550 return;
2551 }
2552 } else {
2553 netdev_warn(usbdev->net, "%s(): could not get configuration.\n",
2554 __func__);
2555 return;
2556 }
2557
2558 /* Get SSID, in case of error, use zero length SSID and ignore error. */
2559 len = sizeof(ssid);
2560 memset(&ssid, 0, sizeof(ssid));
2561 ret = rndis_query_oid(usbdev, OID_802_11_SSID, &ssid, &len);
2562 netdev_dbg(usbdev->net, "%s(): OID_802_11_SSID -> %d, len: %d, ssid: "
2563 "'%.32s'\n", __func__, ret,
2564 le32_to_cpu(ssid.length), ssid.essid);
2565
2566 if (le32_to_cpu(ssid.length) > 32)
2567 ssid.length = cpu_to_le32(32);
2568
2569 ie_buf[0] = WLAN_EID_SSID;
2570 ie_buf[1] = le32_to_cpu(ssid.length);
2571 memcpy(&ie_buf[2], ssid.essid, le32_to_cpu(ssid.length));
2572
2573 ie_len = le32_to_cpu(ssid.length) + 2;
2574
2575 /* no tsf */
2576 timestamp = 0;
2577
2578 netdev_dbg(usbdev->net, "%s(): channel:%d(freq), bssid:[%pM], tsf:%d, "
2579 "capa:%x, beacon int:%d, resp_ie(len:%d, essid:'%.32s'), "
2580 "signal:%d\n", __func__, (channel ? channel->center_freq : -1),
2581 bssid, (u32)timestamp, capability, beacon_interval, ie_len,
2582 ssid.essid, signal);
2583
2584 cfg80211_inform_bss(priv->wdev.wiphy, channel, bssid,
2585 timestamp, capability, beacon_interval, ie_buf, ie_len,
2586 signal, GFP_KERNEL);
2587}
2588
2498/* 2589/*
2499 * workers, indication handlers, device poller 2590 * workers, indication handlers, device poller
2500 */ 2591 */
@@ -2507,6 +2598,7 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev)
2507 u8 *req_ie, *resp_ie; 2598 u8 *req_ie, *resp_ie;
2508 int ret, offset; 2599 int ret, offset;
2509 bool roamed = false; 2600 bool roamed = false;
2601 bool match_bss;
2510 2602
2511 if (priv->infra_mode == NDIS_80211_INFRA_INFRA && priv->connected) { 2603 if (priv->infra_mode == NDIS_80211_INFRA_INFRA && priv->connected) {
2512 /* received media connect indication while connected, either 2604 /* received media connect indication while connected, either
@@ -2558,6 +2650,13 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev)
2558 resp_ie_len = 2650 resp_ie_len =
2559 CONTROL_BUFFER_SIZE - offset; 2651 CONTROL_BUFFER_SIZE - offset;
2560 } 2652 }
2653 } else {
2654 /* Since rndis_wlan_craft_connected_bss() might use info
2655 * later and expects info to contain valid data if
2656 * non-null, free info and set NULL here.
2657 */
2658 kfree(info);
2659 info = NULL;
2561 } 2660 }
2562 } else if (WARN_ON(priv->infra_mode != NDIS_80211_INFRA_ADHOC)) 2661 } else if (WARN_ON(priv->infra_mode != NDIS_80211_INFRA_ADHOC))
2563 return; 2662 return;
@@ -2569,13 +2668,26 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev)
2569 netdev_dbg(usbdev->net, "link up work: [%pM]%s\n", 2668 netdev_dbg(usbdev->net, "link up work: [%pM]%s\n",
2570 bssid, roamed ? " roamed" : ""); 2669 bssid, roamed ? " roamed" : "");
2571 2670
2572 /* Internal bss list in device always contains at least the currently 2671 /* Internal bss list in device should contain at least the currently
2573 * connected bss and we can get it to cfg80211 with 2672 * connected bss and we can get it to cfg80211 with
2574 * rndis_check_bssid_list(). 2673 * rndis_check_bssid_list().
2575 * NOTE: This is true for Broadcom chip, but not mentioned in RNDIS 2674 *
2576 * spec. 2675 * NDIS spec says: "If the device is associated, but the associated
2676 * BSSID is not in its BSSID scan list, then the driver must add an
2677 * entry for the BSSID at the end of the data that it returns in
2678 * response to query of OID_802_11_BSSID_LIST."
2679 *
2680 * NOTE: Seems to be true for BCM4320b variant, but not BCM4320a.
2577 */ 2681 */
2578 rndis_check_bssid_list(usbdev); 2682 match_bss = false;
2683 rndis_check_bssid_list(usbdev, bssid, &match_bss);
2684
2685 if (!is_zero_ether_addr(bssid) && !match_bss) {
2686 /* Couldn't get bss from device, we need to manually craft bss
2687 * for cfg80211.
2688 */
2689 rndis_wlan_craft_connected_bss(usbdev, bssid, info);
2690 }
2579 2691
2580 if (priv->infra_mode == NDIS_80211_INFRA_INFRA) { 2692 if (priv->infra_mode == NDIS_80211_INFRA_INFRA) {
2581 if (!roamed) 2693 if (!roamed)