rndis_wlan: ignore OID_802_11_ADD_KEY triggered media connect indications
PATCH 1/8 rndis_wlan: ignore OID_802_11_ADD_KEY triggered media connect indications
Setting WPA keys with OID_802_11_ADD_KEY sometimes trigger instant media connect indication. These indications are extranous and should be ignored, as otherwise driver would send reassociation event to userspace which in this case is not needed.
---
drivers/net/wireless/rndis_wlan.c | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-)
drivers/net/wireless/rndis_wlan.c +17 -0
@@ -470,6 +470,7 @@ struct rndis_wlan_private {int radio_on;int infra_mode;struct ndis_80211_ssid essid; +__le32 current_command_oid;/* encryption stuff */ int encr_tx_key_index;
@@ -665,7 +666,9 @@ static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len) u.get->msg_len =cpu_to_le32 (sizeof *u.get); u.get->oid = oid; + priv->current_command_oid = oid; ret =rndis_command (dev, u.header, buflen); + priv->current_command_oid =0 ;if (ret <0 ) devdbg(dev, "rndis_query_oid(%s): rndis_command() failed, %d " "(%08x)", oid_to_string(oid), ret,
@@ -725,7 +728,9 @@ static int rndis_set_oid(struct usbnet *dev, __le32 oid, void *data, int len) u.set->handle =cpu_to_le32 (0 );memcpy (u.buf +sizeof (*u.set), data, len); + priv->current_command_oid = oid; ret =rndis_command (dev, u.header, buflen); + priv->current_command_oid =0 ;if (ret <0 ) devdbg(dev, "rndis_set_oid(%s): rndis_command() failed, %d " "(%08x)", oid_to_string(oid), ret,
@@ -760,6 +765,7 @@ static int rndis_reset(struct usbnet *usbdev)memset (reset,0 ,sizeof (*reset)); reset->msg_type =RNDIS_MSG_RESET ; reset->msg_len =cpu_to_le32 (sizeof (*reset)); + priv->current_command_oid =0 ; ret =rndis_command (usbdev, (void *)reset,CONTROL_BUFFER_SIZE );mutex_unlock (&priv->command_lock);
@@ -2558,6 +2564,17 @@ static void rndis_wlan_indication(struct usbnet *usbdev, void *ind, int buflen)switch (msg->status) {case RNDIS_STATUS_MEDIA_CONNECT : +if (priv->current_command_oid ==OID_802_11_ADD_KEY ) { +/* OID_802_11_ADD_KEY causes sometimes extra +* "media connect" indications which confuses driver +* and userspace to think that device is +* roaming/reassociating when it isn't. +*/ +devdbg (usbdev,"ignored OID_802_11_ADD_KEY triggered " +"'media connect'" ); +return ; + } +usbnet_pause_rx (usbdev);devinfo (usbdev,"media connect" );
PATCH 2/8 rndis_wlan: get bssid scan list before new scan
OID_802_11_BSSID_LIST_SCAN clears device's bssid list, so retrieve current bssid list from device before issuing new scan.
---
drivers/net/wireless/rndis_wlan.c | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-)
drivers/net/wireless/rndis_wlan.c +6 -0
@@ -926,6 +926,7 @@ static int freq_to_dsconfig(struct iw_freq *freq, unsigned int *dsconfig)* common functions */ static void restore_keys (struct usbnet *usbdev); +static int rndis_check_bssid_list (struct usbnet *usbdev);static int get_essid (struct usbnet *usbdev,struct ndis_80211_ssid *ssid) {
@@ -1616,6 +1617,11 @@ static int rndis_scan(struct wiphy *wiphy, struct net_device *dev,devdbg (usbdev,"cfg80211.scan" ); +/* Get current bssid list from device before new scan, as new scan +* clears internal bssid list. +*/ +rndis_check_bssid_list (usbdev); +if (!request)return -EINVAL ;
PATCH 3/8 rndis_wlan: resize bssid list if too small
In reply to Jussi KivilinnaBuffer used for bssid list might be too small. Change rndis_query_oid() to return required buffer length to caller and make rndis_check_bssid_list() resize buffer when needed.
---
drivers/net/wireless/rndis_wlan.c | 17 +++++++++++++++-- 1 files changed, 15 insertions(+), 2 deletions(-)
drivers/net/wireless/rndis_wlan.c +15 -2
@@ -676,7 +676,8 @@ static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len)if (ret ==0 ) { ret =le32_to_cpu (u.get_c->len); - *len = (*len > ret) ? ret : *len; +if (ret > *len) + *len = ret;memcpy (data, u.buf +le32_to_cpu (u.get_c->offset) +8 , *len); ret =rndis_error_status (u.get_c->status);
@@ -1656,6 +1657,9 @@ static struct cfg80211_bss *rndis_bss_info_update(struct usbnet *usbdev,int ie_len, bssid_len;u8 *ie; +devdbg (usbdev," found bssid: '%.32s' [%pM]" , bssid->ssid.essid, + bssid->mac); +/* parse bssid structure */ bssid_len =le32_to_cpu (bssid->length);
@@ -1695,10 +1699,12 @@ static int rndis_check_bssid_list(struct usbnet *usbdev)struct ndis_80211_bssid_list_ex *bssid_list;struct ndis_80211_bssid_ex *bssid;int ret = -EINVAL , len, count, bssid_len; +bool resized = false;devdbg (usbdev,"check_bssid_list" ); len =CONTROL_BUFFER_SIZE ; +resize_buf: buf =kmalloc (len,GFP_KERNEL );if (!buf) { ret = -ENOMEM ;
@@ -1709,11 +1715,18 @@ static int rndis_check_bssid_list(struct usbnet *usbdev)if (ret !=0 ) goto out; +if (!resized && len >CONTROL_BUFFER_SIZE ) { + resized = true; +kfree (buf); + goto resize_buf; + } + bssid_list = buf; bssid = bssid_list->bssid; bssid_len =le32_to_cpu (bssid->length); count =le32_to_cpu (bssid_list->num_items); -devdbg (usbdev,"check_bssid_list: %d BSSIDs found" , count); +devdbg (usbdev,"check_bssid_list: %d BSSIDs found (buflen: %d)" , count, + len);while (count && ((void *)bssid + bssid_len) <= (buf + len)) {rndis_bss_info_update (usbdev, bssid);
PATCH 4/8 rndis_wlan: increase scan timer delay
In reply to Jussi KivilinnaIncrease scan delay from 1 sec to 6 sec. Spec says that scan by OID_802_11_BSSID_LIST_SCAN completes in 6 seconds. Before rfkill patch too short delay was not problem as device was always active (radio on) and performing background scanning.
---
drivers/net/wireless/rndis_wlan.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/net/wireless/rndis_wlan.c +1 -1
@@ -1607,7 +1607,7 @@ static int rndis_get_tx_power(struct wiphy *wiphy, int *dbm) } -#define SCAN_DELAY_JIFFIES (HZ) +#define SCAN_DELAY_JIFFIES (6 * HZ)static int rndis_scan (struct wiphy *wiphy,struct net_device *dev,struct cfg80211_scan_request *request) {
PATCH 5/8 rndis_wlan: move link up/down work to separate functions
In reply to Jussi KivilinnaMove link up/down work to separate functions and use local array for allocating memory for info structure instead of kzmalloc.
---
drivers/net/wireless/rndis_wlan.c | 101 ++++++++++++++++++++----------------- 1 files changed, 55 insertions(+), 46 deletions(-)
drivers/net/wireless/rndis_wlan.c +55 -46
@@ -2319,68 +2319,77 @@ static const struct iw_handler_def rndis_iw_handlers = { }; -static void rndis_wlan_worker (struct work_struct *work) +static void rndis_wlan_do_link_up_work (struct usbnet *usbdev) { -struct rndis_wlan_private *priv = - container_of(work, struct rndis_wlan_private, work); -struct usbnet *usbdev = priv->usbdev; -union iwreq_data evt; -unsigned char bssid[ETH_ALEN ];struct ndis_80211_assoc_info *info; -int assoc_size =sizeof (*info) +IW_CUSTOM_MAX +32 ; +union iwreq_data evt; +u8 assoc_buf[sizeof (*info) +IW_CUSTOM_MAX +32 ]; +u8 bssid[ETH_ALEN ];int ret, offset; -if (test_and_clear_bit (WORK_LINK_UP , &priv->work_pending)) { -netif_carrier_on (usbdev->net); - - info =kzalloc (assoc_size,GFP_KERNEL ); -if (!info) - goto get_bssid; - -/* Get association info IEs from device and send them back to -* userspace. */ - ret =get_association_info (usbdev, info, assoc_size); -if (!ret) { - evt.data.length =le32_to_cpu (info->req_ie_length); -if (evt.data.length >0 ) { - offset =le32_to_cpu (info->offset_req_ies); -wireless_send_event (usbdev->net, -IWEVASSOCREQIE , &evt, - (char *)info + offset); - } - - evt.data.length =le32_to_cpu (info->resp_ie_length); -if (evt.data.length >0 ) { - offset =le32_to_cpu (info->offset_resp_ies); -wireless_send_event (usbdev->net, -IWEVASSOCRESPIE , &evt, - (char *)info + offset); - } +memset (assoc_buf,0 ,sizeof (assoc_buf)); + info = (void *)assoc_buf; + +netif_carrier_on (usbdev->net); + +/* Get association info IEs from device and send them back to +* userspace. */ + ret =get_association_info (usbdev, info,sizeof (assoc_buf)); +if (!ret) { + evt.data.length =le32_to_cpu (info->req_ie_length); +if (evt.data.length >0 ) { + offset =le32_to_cpu (info->offset_req_ies); +wireless_send_event (usbdev->net, +IWEVASSOCREQIE , &evt, + (char *)info + offset); } -kfree (info); - -get_bssid: - ret =get_bssid (usbdev, bssid); -if (!ret) { - evt.data.flags =0 ; - evt.data.length =0 ; -memcpy (evt.ap_addr.sa_data, bssid,ETH_ALEN ); -wireless_send_event (usbdev->net,SIOCGIWAP , &evt,NULL ); + evt.data.length =le32_to_cpu (info->resp_ie_length); +if (evt.data.length >0 ) { + offset =le32_to_cpu (info->offset_resp_ies); +wireless_send_event (usbdev->net, +IWEVASSOCRESPIE , &evt, + (char *)info + offset); }usbnet_resume_rx (usbdev); } -if (test_and_clear_bit (WORK_LINK_DOWN , &priv->work_pending)) { -netif_carrier_off (usbdev->net); - + ret =get_bssid (usbdev, bssid); +if (!ret) { evt.data.flags =0 ; evt.data.length =0 ; -memset (evt.ap_addr.sa_data,0 ,ETH_ALEN ); +memcpy (evt.ap_addr.sa_data, bssid,ETH_ALEN );wireless_send_event (usbdev->net,SIOCGIWAP , &evt,NULL ); } +usbnet_resume_rx (usbdev); +} + +static void rndis_wlan_do_link_down_work (struct usbnet *usbdev) +{ +union iwreq_data evt; + +netif_carrier_off (usbdev->net); + + evt.data.flags =0 ; + evt.data.length =0 ; +memset (evt.ap_addr.sa_data,0 ,ETH_ALEN ); +wireless_send_event (usbdev->net,SIOCGIWAP , &evt,NULL ); +} + +static void rndis_wlan_worker (struct work_struct *work) +{ +struct rndis_wlan_private *priv = + container_of(work, struct rndis_wlan_private, work); +struct usbnet *usbdev = priv->usbdev; + +if (test_and_clear_bit (WORK_LINK_UP , &priv->work_pending)) +rndis_wlan_do_link_up_work (usbdev); + +if (test_and_clear_bit (WORK_LINK_DOWN , &priv->work_pending)) +rndis_wlan_do_link_down_work (usbdev); +if (test_and_clear_bit (WORK_SET_MULTICAST_LIST , &priv->work_pending))set_multicast_list (usbdev); }
PATCH 6/8 rndis_wlan: use is_zero_ether_addr() and is_broadcast_ether_addr()
In reply to Jussi KivilinnaUse is_zero_ether_addr() and is_broadcast_ether_addr() instead of memcmp against ffff_bssid/zero_bssid.
---
drivers/net/wireless/rndis_wlan.c | 17 ++++++----------- 1 files changed, 6 insertions(+), 11 deletions(-)
drivers/net/wireless/rndis_wlan.c +6 -11
@@ -514,11 +514,6 @@ static struct cfg80211_ops rndis_config_ops = {static void *rndis_wiphy_privid = &rndis_wiphy_privid; -static const unsigned char zero_bssid[ETH_ALEN ] = {0 ,}; -static const unsigned char ffff_bssid[ETH_ALEN ] = {0xff ,0xff ,0xff , -0xff ,0xff ,0xff }; - -static struct rndis_wlan_private *get_rndis_wlan_priv (struct usbnet *dev) {return (struct rndis_wlan_private *)dev->driver_priv;
@@ -995,7 +990,7 @@ static int is_associated(struct usbnet *usbdev) ret =get_bssid (usbdev, bssid); -return (ret ==0 &&memcmp (bssid, zero_bssid,ETH_ALEN ) !=0 ); +return (ret ==0 && !is_zero_ether_addr (bssid)); }
@@ -1293,8 +1288,8 @@ static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len,devdbg (usbdev,"add_wpa_key: recv seq flag without buffer" );return -EINVAL ; } - is_addr_ok = addr &&memcmp (addr, zero_bssid,ETH_ALEN ) !=0 && -memcmp (addr, ffff_bssid,ETH_ALEN ) !=0 ; + is_addr_ok = addr && !is_zero_ether_addr (addr) && + !is_broadcast_ether_addr (addr);if ((flags &NDIS_80211_ADDKEY_PAIRWISE_KEY ) && !is_addr_ok) {devdbg (usbdev,"add_wpa_key: pairwise but bssid invalid (%pM)" , addr);
@@ -1379,8 +1374,8 @@ static int restore_key(struct usbnet *usbdev, int key_idx)/*if (priv->encr_tx_key_index == key_idx) flags |= NDIS_80211_ADDKEY_TRANSMIT_KEY;*/ -if (memcmp (key.bssid, zero_bssid,ETH_ALEN ) !=0 && -memcmp (key.bssid, ffff_bssid,ETH_ALEN ) !=0 ) +if (!is_zero_ether_addr (key.bssid) && + !is_broadcast_ether_addr (key.bssid)) flags |=NDIS_80211_ADDKEY_PAIRWISE_KEY ; return add_wpa_key(usbdev, key.material, key.len, key_idx,
@@ -1430,7 +1425,7 @@ static int remove_key(struct usbnet *usbdev, int index, u8 bssid[ETH_ALEN]) remove_key.index =cpu_to_le32 (index);if (bssid) {/* pairwise key */ -if (memcmp (bssid, ffff_bssid,ETH_ALEN ) !=0 ) +if (!is_broadcast_ether_addr (bssid)) remove_key.index |=NDIS_80211_ADDKEY_PAIRWISE_KEY ; memcpy(remove_key.bssid, bssid,
PATCH 7/8 rndis_wlan: set ieee80211_ptr->iftype in rndis_change_virtual_intf
In reply to Jussi Kivilinna---
drivers/net/wireless/rndis_wlan.c | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-)
drivers/net/wireless/rndis_wlan.c +4 -1
@@ -1526,7 +1526,8 @@ static int rndis_change_virtual_intf(struct wiphy *wiphy, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { - struct usbnet *usbdev = netdev_priv(dev); + struct rndis_wlan_private *priv = wiphy_priv(wiphy); +struct usbnet *usbdev = priv->usbdev;int mode;switch (type) {
@@ -1540,6 +1541,8 @@ static int rndis_change_virtual_intf(struct wiphy *wiphy,return -EINVAL ; } + priv->wdev.iftype = type; +return set_infra_mode (usbdev, mode); }
PATCH 8/8 rndis_wlan: enable infrastructure before setting random essid
In reply to Jussi KivilinnaRandom essid must be set to turn on radio when not connected. If device is in ad-hoc mode, this results 'media connect' indications with the random essid which should be ignored.
---
drivers/net/wireless/rndis_wlan.c | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-)
drivers/net/wireless/rndis_wlan.c +6 -0
@@ -921,6 +921,7 @@ static int freq_to_dsconfig(struct iw_freq *freq, unsigned int *dsconfig)/* * common functions */ +static int set_infra_mode (struct usbnet *usbdev,int mode);static void restore_keys (struct usbnet *usbdev);static int rndis_check_bssid_list (struct usbnet *usbdev);
@@ -1014,6 +1015,11 @@ static int disassociate(struct usbnet *usbdev, int reset_ssid)/* disassociate causes radio to be turned off; if reset_ssid * is given, set random ssid to enable radio */ if (reset_ssid) { +/* Set device to infrastructure mode so we don't get ad-hoc +* 'media connect' indications with the random ssid. +*/ +set_infra_mode (usbdev,NDIS_80211_INFRA_INFRA ); + ssid.length =cpu_to_le32 (sizeof (ssid.essid));get_random_bytes (&ssid.essid[2 ],sizeof (ssid.essid)-2 ); ssid.essid[0 ] =0x1 ;