Ver código fonte

qcacmn: Don't unlink connected bssid

In dual station case, wlan0 is connected to AP, wlan1
connects to the same ap and will get failed because two
interfaces can not connect to same band (same ap).
At present wlan1 will do unlink bss of ap after connect fail.
This may corrupt the bss tree in kernel because the bss is
still pointed by wlan0’s wdev -> current_bss.
Fix by avoid the unlink bss if the bss is still connected
in any interfaces.

Change-Id: I27c76eca2d4f130addabbdc2600f6959d24c8511
CRs-Fixed: 2837496
Liangwei Dong 4 anos atrás
pai
commit
d4b3e3d043

+ 3 - 2
os_if/linux/mlme/src/osif_cm_util.c

@@ -266,8 +266,9 @@ void osif_cm_unlink_bss(struct wlan_objmgr_vdev *vdev,
 	struct wiphy *wiphy = osif_priv->wdev->wiphy;
 	struct scan_filter *filter;
 
-	__wlan_cfg80211_unlink_bss_list(wiphy, bssid->bytes,
-					ssid_len ? ssid : NULL, ssid_len);
+	__wlan_cfg80211_unlink_bss_list(wiphy, wlan_vdev_get_pdev(vdev),
+					bssid->bytes, ssid_len ? ssid : NULL,
+					ssid_len);
 	filter = qdf_mem_malloc(sizeof(*filter));
 	if (!filter)
 		return;

+ 6 - 3
os_if/linux/scan/inc/wlan_cfg80211_scan.h

@@ -298,14 +298,17 @@ void wlan_cfg80211_inform_bss_frame(struct wlan_objmgr_pdev *pdev,
 /**
  * __wlan_cfg80211_unlink_bss_list() - flush bss from the kernel cache
  * @wiphy: wiphy
+ * @pdev: pdev object
  * @bssid: bssid of the BSS to find
  * @ssid: ssid of the BSS to find
  * @ssid_len: ssid len of of the BSS to find
  *
- * Return: None
+ * Return: QDF_STATUS
  */
-void __wlan_cfg80211_unlink_bss_list(struct wiphy *wiphy, uint8_t *bssid,
-				     uint8_t *ssid, uint8_t ssid_len);
+QDF_STATUS __wlan_cfg80211_unlink_bss_list(struct wiphy *wiphy,
+					   struct wlan_objmgr_pdev *pdev,
+					   uint8_t *bssid, uint8_t *ssid,
+					   uint8_t ssid_len);
 
 /**
  * wlan_cfg80211_get_bss() - Get the bss entry matching the chan, bssid and ssid

+ 91 - 3
os_if/linux/scan/src/wlan_cfg80211_scan.c

@@ -2055,10 +2055,96 @@ struct cfg80211_bss *wlan_cfg80211_get_bss(struct wiphy *wiphy,
 }
 #endif
 
-void __wlan_cfg80211_unlink_bss_list(struct wiphy *wiphy, uint8_t *bssid,
-				     uint8_t *ssid, uint8_t ssid_len)
+/**
+ * struct wlan_check_bssid_context - bssid check context
+ * @bssid: bssid to be checked
+ * @connected: connected by vdev or not
+ * @vdev_id: vdev id of connected vdev
+ */
+struct wlan_check_bssid_context {
+	struct qdf_mac_addr bssid;
+	bool connected;
+	uint8_t vdev_id;
+};
+
+/**
+ * wlan_get_connected_vdev_handler() - check vdev connected on bssid
+ * @psoc: psoc object
+ * @obj: vdev object
+ * @args: handler context
+ *
+ * This function will check whether vdev is connected on bssid or not and
+ * update the result to handler context accordingly.
+ *
+ * Return: void
+ */
+static void wlan_get_connected_vdev_handler(struct wlan_objmgr_psoc *psoc,
+					    void *obj, void *args)
+{
+	struct wlan_objmgr_vdev *vdev = (struct wlan_objmgr_vdev *)obj;
+	struct wlan_check_bssid_context *context =
+				(struct wlan_check_bssid_context *)args;
+	struct qdf_mac_addr bss_peer_mac;
+	enum QDF_OPMODE op_mode;
+
+	if (context->connected)
+		return;
+	op_mode = wlan_vdev_mlme_get_opmode(vdev);
+	if (op_mode != QDF_STA_MODE && op_mode != QDF_P2P_CLIENT_MODE)
+		return;
+	if (wlan_vdev_is_up(vdev) != QDF_STATUS_SUCCESS)
+		return;
+	if (wlan_vdev_get_bss_peer_mac(vdev, &bss_peer_mac) !=
+	    QDF_STATUS_SUCCESS)
+		return;
+	if (qdf_is_macaddr_equal(&bss_peer_mac, &context->bssid)) {
+		context->connected = true;
+		context->vdev_id = wlan_vdev_get_id(vdev);
+	}
+}
+
+/**
+ * wlan_get_connected_vdev_by_bssid() - check/get any vdev connected on bssid
+ * @pdev: pdev object
+ * @bssid: bssid to be checked
+ * @vdev_id: vdev id
+ *
+ * This function will loop through all the vdev in psoc and find/return the
+ * vdev which is connected to bssid provided.
+ *
+ * Return: bool
+ */
+static bool wlan_get_connected_vdev_by_bssid(struct wlan_objmgr_pdev *pdev,
+					     uint8_t *bssid, uint8_t *vdev_id)
+{
+	struct wlan_objmgr_psoc *psoc;
+	struct wlan_check_bssid_context context;
+
+	psoc = wlan_pdev_get_psoc(pdev);
+	qdf_mem_zero(&context, sizeof(struct wlan_check_bssid_context));
+	qdf_mem_copy(context.bssid.bytes, bssid, QDF_MAC_ADDR_SIZE);
+	wlan_objmgr_iterate_obj_list_all(psoc, WLAN_VDEV_OP,
+					 wlan_get_connected_vdev_handler,
+					 &context, true, WLAN_OSIF_SCAN_ID);
+	if (context.connected)
+		*vdev_id = context.vdev_id;
+
+	return context.connected;
+}
+
+QDF_STATUS  __wlan_cfg80211_unlink_bss_list(struct wiphy *wiphy,
+					    struct wlan_objmgr_pdev *pdev,
+					    uint8_t *bssid, uint8_t *ssid,
+					    uint8_t ssid_len)
 {
 	struct cfg80211_bss *bss = NULL;
+	uint8_t vdev_id;
+
+	if (bssid && wlan_get_connected_vdev_by_bssid(pdev, bssid, &vdev_id)) {
+		osif_debug("BSS "QDF_MAC_ADDR_FMT" connected on vdev %d dont unlink",
+			   QDF_MAC_ADDR_REF(bssid), vdev_id);
+		return QDF_STATUS_E_FAILURE;
+	}
 
 	bss = wlan_cfg80211_get_bss(wiphy, NULL, bssid,
 				    ssid, ssid_len);
@@ -2093,6 +2179,8 @@ void __wlan_cfg80211_unlink_bss_list(struct wiphy *wiphy, uint8_t *bssid,
 		/* cfg80211_get_bss get bss with ref count so release it */
 		wlan_cfg80211_put_bss(wiphy, bss);
 	}
+
+	return QDF_STATUS_SUCCESS;
 }
 void wlan_cfg80211_unlink_bss_list(struct wlan_objmgr_pdev *pdev,
 				   struct scan_cache_entry *scan_entry)
@@ -2107,7 +2195,7 @@ void wlan_cfg80211_unlink_bss_list(struct wlan_objmgr_pdev *pdev,
 
 	wiphy = pdev_ospriv->wiphy;
 
-	__wlan_cfg80211_unlink_bss_list(wiphy, scan_entry->bssid.bytes,
+	__wlan_cfg80211_unlink_bss_list(wiphy, pdev, scan_entry->bssid.bytes,
 					scan_entry->ssid.ssid,
 					scan_entry->ssid.length);
 }