Parcourir la source

qcacmn: Handle disconnect ind to kernel for connection manager

Add support in connection manager for disconnect done indication
to kernel.

Change-Id: Ia4e767a95403de04268ed8c5e424974788c90b17
CRs-Fixed: 2765978
Ashish Kumar Dhanotiya il y a 4 ans
Parent
commit
45a6ac131e

+ 18 - 0
os_if/linux/mlme/inc/wlan_cfg80211_cm_rsp.h

@@ -24,4 +24,22 @@
 #ifndef __WLAN_CFG80211_CM_RSP_H
 #define __WLAN_CFG80211_CM_RSP_H
 
+#include "wlan_objmgr_vdev_obj.h"
+#include "wlan_cm_public_struct.h"
+
+/**
+ * osif_disconnect_handler() - Indicate disconnnect to userspace
+ * @vdev: vdev pointer
+ * @rsp: Disconnect response from connection manager
+ *
+ * This function indicates disconnect to the kernel which thus indicates
+ * to the userspace.
+ *
+ * Context: Any context
+ * Return: QDF_STATUS_SUCCESS on successful indication to kernel,
+ * else QDF_STATUS with failure reason
+ */
+QDF_STATUS osif_disconnect_handler(struct wlan_objmgr_vdev *vdev,
+				   struct wlan_cm_discon_rsp *rsp);
+
 #endif /* __WLAN_CFG80211_CM_RSP_H */

+ 29 - 0
os_if/linux/mlme/inc/wlan_cfg80211_cm_util.h

@@ -31,4 +31,33 @@
  * Return: QDF_STATUS
  */
 QDF_STATUS osif_cm_register_cb(void);
+
+/**
+ * osif_cm_reset_id_and_src_no_lock() - Function to resets last
+ * connection manager command id and source in osif
+ * @osif_priv: Pointer to vdev osif priv
+ *
+ * This function resets the last connection manager command id
+ * and source.
+ *
+ * Context: Any context. This function should be called by holding
+ * cmd id spinlock
+ * Return: None
+ */
+
+void osif_cm_reset_id_and_src_no_lock(struct vdev_osif_priv *osif_priv);
+
+/**
+ * osif_cm_reset_id_and_src() - Function to resets last
+ * connection manager command id and source in osif
+ * @vdev: vdev pointer
+ *
+ * This function resets the last connection manager command id
+ * and source.
+ *
+ * Context: Any context. Takes and release cmd id spinlock
+ * Return: None
+ */
+QDF_STATUS osif_cm_reset_id_and_src(struct wlan_objmgr_vdev *vdev);
+
 #endif /* __WLAN_CFG80211_CM_UTIL_H */

+ 106 - 0
os_if/linux/mlme/src/wlan_cfg80211_cm_disconnect_rsp.c

@@ -20,3 +20,109 @@
  * This file maintains definitaions of disconnect response
  * fucntions.
  */
+
+#include <wlan_cfg80211.h>
+#include <linux/wireless.h>
+#include "wlan_cfg80211_cm_rsp.h"
+#include "wlan_osif_priv.h"
+#include "wlan_cfg80211_cm_util.h"
+
+/**
+ * osif_validate_disconnect_and_reset_src_id() - Validate disconnection
+ * and resets source and id
+ * @osif_priv: Pointer to vdev osif priv
+ * @rsp: Disconnect response from connectin manager
+ *
+ * This function validates disconnect response and if the disconnect
+ * response is valid, resets the source and id of the command
+ *
+ * Context: Any context. Takes and releases cmd id spinlock.
+ * Return: QDF_STATUS
+ */
+
+static QDF_STATUS
+osif_validate_disconnect_and_reset_src_id(struct vdev_osif_priv *osif_priv,
+					  struct wlan_cm_discon_rsp *rsp)
+{
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	/* Always drop internal disconnect */
+	qdf_spinlock_acquire(&osif_priv->last_cmd_info.cmd_id_lock);
+	if (rsp->req.req.source == CM_INTERNAL_DISCONNECT) {
+		osif_debug("ignore internal disconnect");
+		status = QDF_STATUS_E_INVAL;
+		goto rel_lock;
+	}
+
+	/*
+	 * Send to kernel only if last osif cmd type is disconnect and
+	 * cookie match else drop. If cookie match reset the cookie
+	 * and source
+	 */
+	if (rsp->req.cm_id != osif_priv->last_cmd_info.last_id ||
+	    rsp->req.req.source != osif_priv->last_cmd_info.last_source) {
+		osif_debug("Ignore as cm_id(%d)/src(%d) didn't match stored cm_id(%d)/src(%d)",
+			   rsp->req.cm_id, rsp->req.req.source,
+			   osif_priv->last_cmd_info.last_id,
+			   osif_priv->last_cmd_info.last_source);
+		status = QDF_STATUS_E_INVAL;
+		goto rel_lock;
+	}
+
+	osif_cm_reset_id_and_src_no_lock(osif_priv);
+rel_lock:
+	qdf_spinlock_release(&osif_priv->last_cmd_info.cmd_id_lock);
+
+	return status;
+}
+
+#if defined(CFG80211_DISCONNECTED_V2) || \
+(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0))
+static void
+osif_cm_indicate_disconnect(struct net_device *dev,
+			    enum ieee80211_reasoncode reason,
+			    bool locally_generated, const u8 *ie,
+			    size_t ie_len, gfp_t gfp)
+{
+	cfg80211_disconnected(dev, reason, ie, ie_len, locally_generated, gfp);
+}
+#else
+static void
+osif_cm_indicate_disconnect(struct net_device *dev,
+			    enum ieee80211_reasoncode reason,
+			    bool locally_generated, const u8 *ie,
+			    size_t ie_len, gfp_t gfp)
+{
+	cfg80211_disconnected(dev, reason, ie, ie_len, gfp);
+}
+#endif
+
+QDF_STATUS osif_disconnect_handler(struct wlan_objmgr_vdev *vdev,
+				   struct wlan_cm_discon_rsp *rsp)
+{
+	enum ieee80211_reasoncode ieee80211_reason = rsp->req.req.reason_code;
+	struct vdev_osif_priv *osif_priv = wlan_vdev_get_ospriv(vdev);
+	bool locally_generated = true;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	osif_info("%s(vdevid-%d): Disconnected, bssid: " QDF_MAC_ADDR_FMT " cm_id %d source %d reason_code %d locally_generated %d",
+		  osif_priv->wdev->netdev->name,
+		  rsp->req.req.vdev_id,
+		  QDF_MAC_ADDR_REF(rsp->req.req.bssid.bytes),
+		  rsp->req.cm_id, rsp->req.req.source,
+		  rsp->req.req.reason_code,
+		  locally_generated);
+
+	status = osif_validate_disconnect_and_reset_src_id(osif_priv, rsp);
+	if (QDF_IS_STATUS_ERROR(status))
+		return status;
+
+	if (rsp->req.req.source == CM_PEER_DISCONNECT)
+		locally_generated = false;
+
+	osif_cm_indicate_disconnect(osif_priv->wdev->netdev, ieee80211_reason,
+				    locally_generated, rsp->ap_discon_ie.ptr,
+				    rsp->ap_discon_ie.len, GFP_KERNEL);
+
+	return status;
+}

+ 13 - 4
os_if/linux/mlme/src/wlan_cfg80211_cm_req.c

@@ -25,6 +25,7 @@
 #include "wlan_cm_ucfg_api.h"
 #include "wlan_nl_to_crypto_params.h"
 #include <wlan_cfg80211.h>
+#include "wlan_cfg80211_cm_util.h"
 
 static void wlan_osif_free_wep_key_params(
 				struct wlan_cm_connect_req *connect_req)
@@ -319,6 +320,10 @@ int wlan_osif_cfg80211_connect(struct net_device *dev,
 
 	osif_dump_connect_req(dev, vdev_id, req);
 
+	status = osif_cm_reset_id_and_src(vdev);
+	if (QDF_IS_STATUS_ERROR(status))
+		return qdf_status_to_os_return(status);
+
 	connect_req = qdf_mem_malloc(sizeof(*connect_req));
 	if (!connect_req)
 		return -ENOMEM;
@@ -398,14 +403,18 @@ int wlan_osif_cfg80211_disconnect(struct net_device *dev,
 	uint8_t vdev_id = vdev->vdev_objmgr.vdev_id;
 	QDF_STATUS status;
 
-	req = qdf_mem_malloc(sizeof(*req));
-	if (!req)
-		return -ENOMEM;
-
 	/* print reason in string */
 	osif_info("%s(vdevid-%d): Received Disconnect reason:%d",
 		  dev->name, vdev_id, reason);
 
+	status = osif_cm_reset_id_and_src(vdev);
+	if (QDF_IS_STATUS_ERROR(status))
+		return qdf_status_to_os_return(status);
+
+	req = qdf_mem_malloc(sizeof(*req));
+	if (!req)
+		return -ENOMEM;
+
 	req->vdev_id = vdev_id;
 	req->source = CM_OSIF_DISCONNECT;
 	req->reason_code = reason;

+ 26 - 3
os_if/linux/mlme/src/wlan_cfg80211_cm_util.c

@@ -24,6 +24,28 @@
 #include "wlan_cfg80211_cm_util.h"
 #include "wlan_osif_priv.h"
 #include "wlan_cfg80211.h"
+#include "wlan_cfg80211_cm_rsp.h"
+
+void osif_cm_reset_id_and_src_no_lock(struct vdev_osif_priv *osif_priv)
+{
+	osif_priv->last_cmd_info.last_id = CM_ID_INVALID;
+	osif_priv->last_cmd_info.last_source = CM_SOURCE_INVALID;
+}
+
+QDF_STATUS osif_cm_reset_id_and_src(struct wlan_objmgr_vdev *vdev)
+{
+	struct vdev_osif_priv *osif_priv = wlan_vdev_get_ospriv(vdev);
+
+	if (!osif_priv) {
+		osif_err("Invalid vdev osif priv");
+		return QDF_STATUS_E_INVAL;
+	}
+	qdf_spinlock_acquire(&osif_priv->last_cmd_info.cmd_id_lock);
+	osif_cm_reset_id_and_src_no_lock(osif_priv);
+	qdf_spinlock_release(&osif_priv->last_cmd_info.cmd_id_lock);
+
+	return QDF_STATUS_SUCCESS;
+}
 
 /**
  * osif_cm_connect_complete_cb() - Connect complete callback
@@ -85,16 +107,17 @@ osif_cm_update_id_and_src_cb(struct wlan_objmgr_vdev *vdev,
 /**
  * osif_cm_disconnect_complete_cb() - Disconnect done callback
  * @vdev: vdev pointer
- * @cm_disconnect_rsp: Disconnect response
+ * @disconnect_rsp: Disconnect response
  *
+ * Context: Any context
  * Return: QDF_STATUS
  */
 
 static QDF_STATUS
 osif_cm_disconnect_complete_cb(struct wlan_objmgr_vdev *vdev,
-			       struct wlan_cm_discon_rsp *cm_disconnect_rsp)
+			       struct wlan_cm_discon_rsp *rsp)
 {
-	return QDF_STATUS_SUCCESS;
+	return osif_disconnect_handler(vdev, rsp);
 }
 
 /**

+ 5 - 0
umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_public_struct.h

@@ -27,6 +27,7 @@
 #include <wlan_scan_public_structs.h>
 #include "wlan_crypto_global_def.h"
 
+#define CM_ID_INVALID 0xFFFFFFFF
 typedef uint32_t wlan_cm_id;
 
 /**
@@ -109,6 +110,8 @@ struct wlan_fils_con_info {
  * @CM_INTERNAL_DISCONNECT: Internal disconnect initiated by Connection manager
  * on receiving the back to back commands
  * @CM_ROAM_DISCONNECT: Disconnect req due to HO failure
+ * @CM_SOURCE_MAX: max value of connection manager source
+ * @CM_SOURCE_INVALID: Invalid connection manager req source
  */
 enum wlan_cm_source {
 	CM_OSIF_CONNECT_REQ,
@@ -118,6 +121,8 @@ enum wlan_cm_source {
 	CM_SB_DISCONNECT,
 	CM_INTERNAL_DISCONNECT,
 	CM_ROAM_DISCONNECT,
+	CM_SOURCE_MAX,
+	CM_SOURCE_INVALID = CM_SOURCE_MAX,
 };
 
 /**