Skip to contentSkip to conversation

rndis_wlan: ignore OID_802_11_ADD_KEY triggered media connect indications

PATCH New Jussi Kivilinna 1 participant 8 messages Aug 28, 2009 Networking drivers (wireless)

lore
Share & tools
lore
#0 lore raw

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
index f181b00..612c2c7 100644
@@ -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");
#1 lore raw

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
index 612c2c7..3779621 100644
@@ -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;
 
#2 lore raw

PATCH 3/8 rndis_wlan: resize bssid list if too small

In reply to Jussi Kivilinna

Buffer 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
index 3779621..2309ad2 100644
@@ -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);
#3 lore raw

PATCH 4/8 rndis_wlan: increase scan timer delay

In reply to Jussi Kivilinna

Increase 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
index 2309ad2..cb362b0 100644
@@ -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)
 {
#4 lore raw

PATCH 5/8 rndis_wlan: move link up/down work to separate functions

In reply to Jussi Kivilinna

Move 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
index cb362b0..dc3083b 100644
@@ -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);
 }
#5 lore raw

PATCH 6/8 rndis_wlan: use is_zero_ether_addr() and is_broadcast_ether_addr()

In reply to Jussi Kivilinna

Use 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
index dc3083b..c28fde5 100644
@@ -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,
#6 lore raw

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
index c28fde5..d117620 100644
@@ -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);
 }
 
#7 lore raw

PATCH 8/8 rndis_wlan: enable infrastructure before setting random essid

In reply to Jussi Kivilinna

Random 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
index d117620..c5a674d 100644
@@ -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;