فهرست منبع

qcacmn: Handle disconnect requests in different states

Handle disconnect requests in different states.
Take care of flushing scan and older connect and queueing
internal disconnect depending on the current state.

Change-Id: I08015d866fb629951b85f95fcd3cf0155e902082
CRs-Fixed: 2793228
gaurank kathpalia 4 سال پیش
والد
کامیت
7d55cb743a

+ 19 - 7
umac/mlme/connection_mgr/core/src/wlan_cm_connect.c

@@ -485,11 +485,20 @@ static QDF_STATUS cm_connect_get_candidates(struct wlan_objmgr_pdev *pdev,
 		 * if candidates were found and were removed due to invalid
 		 * return failure
 		 */
-		if (!num_bss)
-			return cm_sm_deliver_event_sync(cm_ctx,
-							WLAN_CM_SM_EV_SCAN,
-							sizeof(*cm_req),
-							cm_req);
+		if (!num_bss) {
+			QDF_STATUS status;
+
+			status = cm_sm_deliver_event_sync(cm_ctx,
+							  WLAN_CM_SM_EV_SCAN,
+							  sizeof(*cm_req),
+							  cm_req);
+			/*
+			 * If connect scan is initiated, return pending, so that
+			 * connect start after scan complete
+			 */
+			if (QDF_IS_STATUS_SUCCESS(status))
+				return QDF_STATUS_E_PENDING;
+		}
 
 		return QDF_STATUS_E_EMPTY;
 	}
@@ -589,12 +598,12 @@ cm_handle_connect_req_in_non_init_state(struct cnx_mgr *cm_ctx,
 		 *
 		 * 1. There is a connect request pending, so just remove
 		 *    the pending connect req. As we will queue a new connect
-		 *    req all resp for pending conenct req will be dropped.
+		 *    req, all resp for pending connect req will be dropped.
 		 * 2. There is a connect request in active and
 		 *    and a internal disconnect followed by a connect req in
 		 *    pending. In this case the disconnect will take care of
 		 *    cleaning up the active connect request and thus only
-		 *    remove the pending conenct.
+		 *    remove the pending connect.
 		 */
 		cm_flush_pending_request(cm_ctx, CONNECT_REQ_PREFIX);
 		break;
@@ -650,6 +659,9 @@ QDF_STATUS cm_connect_start(struct cnx_mgr *cm_ctx,
 	mlme_cm_connect_start_ind(cm_ctx->vdev, &cm_req->req);
 
 	status = cm_connect_get_candidates(pdev, cm_ctx, cm_req);
+	/* In case of status pending connect will continue after scan */
+	if (status == QDF_STATUS_E_PENDING)
+		return QDF_STATUS_SUCCESS;
 	if (QDF_IS_STATUS_ERROR(status)) {
 		reason = CM_NO_CANDIDATE_FOUND;
 		goto connect_err;

+ 94 - 10
umac/mlme/connection_mgr/core/src/wlan_cm_disconnect.c

@@ -26,8 +26,7 @@
 #include "wlan_blm_api.h"
 #endif
 
-static void
-cm_send_disconnect_resp(struct cnx_mgr *cm_ctx, wlan_cm_id cm_id)
+void cm_send_disconnect_resp(struct cnx_mgr *cm_ctx, wlan_cm_id cm_id)
 {
 	struct wlan_cm_discon_rsp resp;
 	QDF_STATUS status;
@@ -206,8 +205,7 @@ cm_inform_if_mgr_disconnect_start(struct wlan_objmgr_vdev *vdev)
 }
 #endif
 
-void
-cm_initiate_internal_disconnect(struct cnx_mgr *cm_ctx)
+void cm_initiate_internal_disconnect(struct cnx_mgr *cm_ctx)
 {
 	struct cm_req *cm_req;
 	struct cm_disconnect_req *disconnect_req;
@@ -231,9 +229,7 @@ cm_initiate_internal_disconnect(struct cnx_mgr *cm_ctx)
 		return;
 	}
 
-	status = cm_disconnect_start(cm_ctx, disconnect_req);
-	if (QDF_IS_STATUS_ERROR(status))
-		qdf_mem_free(cm_req);
+	cm_disconnect_start(cm_ctx, disconnect_req);
 }
 
 QDF_STATUS cm_disconnect_start(struct cnx_mgr *cm_ctx,
@@ -243,11 +239,12 @@ QDF_STATUS cm_disconnect_start(struct cnx_mgr *cm_ctx,
 	QDF_STATUS status;
 
 	pdev = wlan_vdev_get_pdev(cm_ctx->vdev);
-	if (!pdev)
+	if (!pdev) {
+		cm_send_disconnect_resp(cm_ctx, req->cm_id);
 		return QDF_STATUS_E_INVAL;
+	}
 
-	 /* disconnect TDLS, P2P roc cleanup */
-
+	/* disconnect TDLS, P2P roc cleanup */
 	cm_inform_if_mgr_disconnect_start(cm_ctx->vdev);
 	cm_vdev_scan_cancel(wlan_vdev_get_pdev(cm_ctx->vdev), cm_ctx->vdev);
 	mlme_cm_osif_disconnect_start_ind(cm_ctx->vdev);
@@ -387,11 +384,98 @@ QDF_STATUS cm_disconnect_complete(struct cnx_mgr *cm_ctx,
 
 	cm_inform_blm_disconnect_complete(cm_ctx->vdev, resp);
 
+	/*
+	 * Remove all pending disconnect if this is an active disconnect
+	 * complete.
+	 */
+	if (resp->req.cm_id == cm_ctx->active_cm_id)
+		cm_flush_pending_request(cm_ctx, DISCONNECT_REQ_PREFIX);
+
 	cm_remove_cmd(cm_ctx, resp->req.cm_id);
 
 	return QDF_STATUS_SUCCESS;
 }
 
+QDF_STATUS
+cm_handle_discon_req_in_non_connected_state(struct cnx_mgr *cm_ctx,
+					struct cm_disconnect_req *cm_req,
+					enum wlan_cm_sm_state cm_state_substate)
+{
+	enum wlan_cm_sm_state cur_state = cm_get_state(cm_ctx);
+
+	/*
+	 * South bound and peer disconnect requests are meant for only in
+	 * connected state, so if the state is connecting a new connect has
+	 * been received, hence skip the non-osif disconnect request.
+	 */
+	if (cur_state == WLAN_CM_S_CONNECTING &&
+	    cm_req->req.source != CM_OSIF_DISCONNECT) {
+		mlme_info("Vdev %d ignore disconnect req from source %d in state %d",
+			  wlan_vdev_get_id(cm_ctx->vdev), cm_req->req.source,
+			  cm_state_substate);
+		return QDF_STATUS_E_INVAL;
+	}
+
+	switch (cm_state_substate) {
+	case WLAN_CM_S_DISCONNECTING:
+		/*
+		 * There would be pending disconnect requests in the list, and
+		 * if they are flushed as part of new disconnect
+		 * (cm_flush_pending_request), OS_IF would inform the kernel
+		 * about the disconnect done even though the disconnect is still
+		 * pending. So update OS_IF with invalid CM_ID so that the resp
+		 * of only the new disconnect req is given to kernel.
+		 */
+		mlme_cm_osif_update_id_and_src(cm_ctx->vdev,
+					       CM_SOURCE_INVALID,
+					       CM_ID_INVALID);
+		cm_flush_pending_request(cm_ctx, DISCONNECT_REQ_PREFIX);
+		break;
+	case WLAN_CM_SS_JOIN_ACTIVE:
+		/*
+		 * In join active state, there would be no pending command, so
+		 * for new disconnect request, queue disconnect.
+		 * In disconnecting state queue the new disconnect request, and
+		 * when the old diconnect moves the SM to init it would be
+		 * dropped and required callbacks would be called.
+		 */
+		break;
+	case WLAN_CM_SS_SCAN:
+		/* In the scan state abort the ongoing scan */
+		cm_vdev_scan_cancel(wlan_vdev_get_pdev(cm_ctx->vdev),
+				    cm_ctx->vdev);
+		/* fallthrough */
+	case WLAN_CM_SS_JOIN_PENDING:
+		/*
+		 * There would be pending disconnect requests in the list, and
+		 * if they are flushed as part of new disconnect
+		 * (cm_flush_pending_request), OS_IF would inform the kernel
+		 * about the disconnect done even though the disconnect is still
+		 * pending. So update OS_IF with invalid CM_ID so that the resp
+		 * of only the new disconnect req is given to kernel.
+		 */
+		mlme_cm_osif_update_id_and_src(cm_ctx->vdev,
+					       CM_SOURCE_INVALID,
+					       CM_ID_INVALID);
+		/*
+		 * In case of scan or join pending there could be a connect and
+		 * disconnect requests pending, so flush all the requests except
+		 * the activated request.
+		 */
+		cm_flush_pending_request(cm_ctx, CONNECT_REQ_PREFIX);
+		cm_flush_pending_request(cm_ctx, DISCONNECT_REQ_PREFIX);
+		break;
+	default:
+		mlme_err("Vdev %d disconnect req in invalid state %d",
+			 wlan_vdev_get_id(cm_ctx->vdev),
+			 cm_state_substate);
+		return QDF_STATUS_E_FAILURE;
+	};
+
+	/* Queue the new disconnect request after state specific actions */
+	return cm_add_disconnect_req_to_list(cm_ctx, cm_req);
+}
+
 QDF_STATUS cm_add_disconnect_req_to_list(struct cnx_mgr *cm_ctx,
 					 struct cm_disconnect_req *req)
 {

+ 29 - 0
umac/mlme/connection_mgr/core/src/wlan_cm_main_api.h

@@ -68,6 +68,23 @@ cm_handle_connect_req_in_non_init_state(struct cnx_mgr *cm_ctx,
 					struct cm_connect_req *cm_req,
 					enum wlan_cm_sm_state cm_state_substate);
 
+/**
+ * cm_handle_discon_req_in_non_connected_state() - Handle disconnect req in non
+ * connected state.
+ * @cm_ctx: connection manager context
+ * @cm_req: cm request
+ * @cm_state_substate: state of CM SM
+ *
+ * Context: Can be called only while handling connection manager event
+ *          ie holding state machine lock
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS
+cm_handle_discon_req_in_non_connected_state(struct cnx_mgr *cm_ctx,
+					struct cm_disconnect_req *cm_req,
+					enum wlan_cm_sm_state cm_state_substate);
+
 /**
  * cm_connect_scan_start() - This API will be called to initiate the connect
  * scan if no candidate are found in scan db.
@@ -357,6 +374,18 @@ QDF_STATUS cm_disconnect_rsp(struct wlan_objmgr_vdev *vdev,
  */
 void cm_initiate_internal_disconnect(struct cnx_mgr *cm_ctx);
 
+/**
+ * cm_send_disconnect_resp() - Initiate disconnect resp for the cm_id
+ * @cm_ctx: connection manager context
+ * @cm_id: cm id to send disconnect resp for
+ *
+ * Context: Can be called from any context. Hold the SM lock while calling this
+ * api.
+ *
+ * Return: void
+ */
+void cm_send_disconnect_resp(struct cnx_mgr *cm_ctx, wlan_cm_id cm_id);
+
 /*************** UTIL APIs ****************/
 
 /**

+ 93 - 17
umac/mlme/connection_mgr/core/src/wlan_cm_sm.c

@@ -93,36 +93,49 @@ static bool cm_state_init_event(void *ctx, uint16_t event,
 				uint16_t data_len, void *data)
 {
 	struct cnx_mgr *cm_ctx = ctx;
-	bool status;
-	QDF_STATUS qdf_status;
+	bool event_handled;
+	QDF_STATUS status;
+	struct cm_disconnect_req *req;
 
 	switch (event) {
 	case WLAN_CM_SM_EV_CONNECT_REQ:
-		qdf_status = cm_add_connect_req_to_list(cm_ctx, data);
-		if (QDF_IS_STATUS_ERROR(qdf_status)) {
+		status = cm_add_connect_req_to_list(cm_ctx, data);
+		if (QDF_IS_STATUS_ERROR(status)) {
 			/* if fail to add req return failure */
-			status = false;
+			event_handled = false;
 			break;
 		}
 		cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTING);
 		cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_CONNECT_START,
 					 data_len, data);
-		status = true;
+		event_handled = true;
 		break;
 	case WLAN_CM_SM_EV_CONNECT_FAILURE:
 		cm_connect_complete(cm_ctx, data);
-		status = true;
+		event_handled = true;
 		break;
 	case WLAN_CM_SM_EV_DISCONNECT_DONE:
 		cm_disconnect_complete(cm_ctx, data);
-		status = true;
+		event_handled = true;
+		break;
+	case WLAN_CM_SM_EV_DISCONNECT_REQ:
+		status = cm_add_disconnect_req_to_list(cm_ctx, data);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			/* if fail to add req return failure */
+			event_handled = false;
+			break;
+		}
+
+		req = data;
+		cm_send_disconnect_resp(cm_ctx, req->cm_id);
+		event_handled = true;
 		break;
 	default:
-		status = false;
+		event_handled = false;
 		break;
 	}
 
-	return status;
+	return event_handled;
 }
 
 /**
@@ -167,20 +180,20 @@ static bool cm_state_connecting_event(void *ctx, uint16_t event,
 				      uint16_t data_len, void *data)
 {
 	struct cnx_mgr *cm_ctx = ctx;
-	bool status;
+	bool event_handled;
 
 	switch (event) {
 	case WLAN_CM_SM_EV_CONNECT_START:
 		cm_sm_transition_to(cm_ctx, WLAN_CM_SS_JOIN_PENDING);
 		cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
-		status = true;
+		event_handled = true;
 		break;
 	default:
-		status = false;
+		event_handled = false;
 		break;
 	}
 
-	return status;
+	return event_handled;
 }
 
 /**
@@ -236,12 +249,16 @@ static bool cm_state_connected_event(void *ctx, uint16_t event,
 			event_handled = false;
 			break;
 		}
-		cm_sm_transition_to(cm_ctx, WLAN_CM_SS_JOIN_PENDING);
+		cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTING);
 		cm_sm_deliver_event_sync(cm_ctx,
 					 WLAN_CM_SM_EV_CONNECT_START,
 					 data_len, data);
 		event_handled = true;
 		break;
+	case WLAN_CM_SM_EV_DISCONNECT_ACTIVE:
+		cm_disconnect_active(cm_ctx, data);
+		event_handled = true;
+		break;
 	case WLAN_CM_SM_EV_CONNECT_SUCCESS:
 		cm_connect_complete(cm_ctx, data);
 		event_handled = true;
@@ -319,7 +336,7 @@ static bool cm_state_disconnecting_event(void *ctx, uint16_t event,
 			event_handled = false;
 			break;
 		}
-		cm_sm_transition_to(cm_ctx, WLAN_CM_SS_JOIN_PENDING);
+		cm_sm_transition_to(cm_ctx, WLAN_CM_S_CONNECTING);
 		cm_sm_deliver_event_sync(cm_ctx,
 					 WLAN_CM_SM_EV_CONNECT_START,
 					 data_len, data);
@@ -338,6 +355,18 @@ static bool cm_state_disconnecting_event(void *ctx, uint16_t event,
 		cm_sm_deliver_event_sync(cm_ctx, event, data_len, data);
 		event_handled = true;
 		break;
+	case WLAN_CM_SM_EV_DISCONNECT_REQ:
+		status = cm_handle_discon_req_in_non_connected_state(cm_ctx,
+						data, WLAN_CM_S_DISCONNECTING);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			event_handled = false;
+			break;
+		}
+		cm_sm_deliver_event_sync(cm_ctx,
+					 WLAN_CM_SM_EV_DISCONNECT_START,
+					 data_len, data);
+		event_handled = true;
+		break;
 	default:
 		event_handled = false;
 		break;
@@ -459,6 +488,23 @@ static bool cm_subst_join_pending_event(void *ctx, uint16_t event,
 		cm_disconnect_active(cm_ctx, data);
 		event_handled = true;
 		break;
+	case WLAN_CM_SM_EV_DISCONNECT_DONE:
+		cm_disconnect_complete(cm_ctx, data);
+		event_handled = true;
+		break;
+	case WLAN_CM_SM_EV_DISCONNECT_REQ:
+		status = cm_handle_discon_req_in_non_connected_state(cm_ctx,
+						data, WLAN_CM_SS_JOIN_PENDING);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			event_handled = false;
+			break;
+		}
+		cm_sm_transition_to(cm_ctx, WLAN_CM_S_DISCONNECTING);
+		cm_sm_deliver_event_sync(cm_ctx,
+					 WLAN_CM_SM_EV_DISCONNECT_START,
+					 data_len, data);
+		event_handled = true;
+		break;
 	default:
 		event_handled = false;
 		break;
@@ -548,6 +594,23 @@ static bool cm_subst_scan_event(void *ctx, uint16_t event,
 		cm_disconnect_active(cm_ctx, data);
 		event_handled = true;
 		break;
+	case WLAN_CM_SM_EV_DISCONNECT_DONE:
+		cm_disconnect_complete(cm_ctx, data);
+		event_handled = true;
+		break;
+	case WLAN_CM_SM_EV_DISCONNECT_REQ:
+		status = cm_handle_discon_req_in_non_connected_state(cm_ctx,
+						data, WLAN_CM_SS_SCAN);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			event_handled = false;
+			break;
+		}
+		cm_sm_transition_to(cm_ctx, WLAN_CM_S_DISCONNECTING);
+		cm_sm_deliver_event_sync(cm_ctx,
+					 WLAN_CM_SM_EV_DISCONNECT_START,
+					 data_len, data);
+		event_handled = true;
+		break;
 	default:
 		event_handled = false;
 		break;
@@ -669,6 +732,19 @@ static bool cm_subst_join_active_event(void *ctx, uint16_t event,
 		cm_resume_connect_after_peer_create(cm_ctx, data);
 		event_handled = true;
 		break;
+	case WLAN_CM_SM_EV_DISCONNECT_REQ:
+		status = cm_handle_discon_req_in_non_connected_state(cm_ctx,
+						data, WLAN_CM_SS_JOIN_ACTIVE);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			event_handled = false;
+			break;
+		}
+		cm_sm_transition_to(cm_ctx, WLAN_CM_S_DISCONNECTING);
+		cm_sm_deliver_event_sync(cm_ctx,
+					 WLAN_CM_SM_EV_DISCONNECT_START,
+					 data_len, data);
+		event_handled = true;
+		break;
 	default:
 		event_handled = false;
 		break;
@@ -841,7 +917,7 @@ static const char *cm_sm_event_names[] = {
 	"EV_CONNECT_ACTIVE",
 	"EV_CONNECT_SUCCESS",
 	"EV_BSS_SELECT_IND_SUCCESS",
-	"EV_BSS_CREATE_PEER_SUCCESS"
+	"EV_BSS_CREATE_PEER_SUCCESS",
 	"EV_CONNECT_GET_NXT_CANDIDATE",
 	"EV_CONNECT_FAILURE",
 	"EV_DISCONNECT_REQ",

+ 28 - 9
umac/mlme/connection_mgr/core/src/wlan_cm_util.c

@@ -242,8 +242,8 @@ cm_fill_connect_resp_from_req(struct wlan_cm_connect_rsp *resp,
 }
 
 /**
- * cm_connect_inform_os_if_connect_complete() - Fill fail connect resp from req
- * and indicate same to osif
+ * cm_handle_connect_flush() - Fill fail connect resp from req and indicate
+ * same to osif
  * @cm_ctx: connection manager context
  * @cm_req: cm request
  *
@@ -252,8 +252,7 @@ cm_fill_connect_resp_from_req(struct wlan_cm_connect_rsp *resp,
  * Return: void
  */
 static void
-cm_connect_inform_os_if_connect_complete(struct cnx_mgr *cm_ctx,
-					 struct cm_req *cm_req)
+cm_handle_connect_flush(struct cnx_mgr *cm_ctx, struct cm_req *cm_req)
 {
 	struct wlan_cm_connect_rsp *resp;
 
@@ -273,6 +272,27 @@ cm_connect_inform_os_if_connect_complete(struct cnx_mgr *cm_ctx,
 	qdf_mem_free(resp);
 }
 
+/**
+ * cm_handle_disconnect_flush() - Fill disconnect resp from req and indicate
+ * same to osif
+ * @cm_ctx: connection manager context
+ * @cm_req: cm request
+ *
+ * Context: Can be called from APIs holding cm request list lock
+ *
+ * Return: void
+ */
+static void
+cm_handle_disconnect_flush(struct cnx_mgr *cm_ctx, struct cm_req *cm_req)
+{
+	struct wlan_cm_discon_rsp resp;
+
+	resp.req.cm_id = cm_req->cm_id;
+	resp.req.req = cm_req->discon_req.req;
+
+	mlme_cm_osif_disconnect_complete(cm_ctx->vdev, &resp);
+}
+
 static void cm_remove_cmd_from_serialization(struct cnx_mgr *cm_ctx,
 					     wlan_cm_id cm_id)
 {
@@ -320,8 +340,8 @@ cm_flush_pending_request(struct cnx_mgr *cm_ctx, uint32_t flush_prefix)
 
 		prefix = CM_ID_GET_PREFIX(cm_req->cm_id);
 
-		/* Only remove the pending requests matching the prefix */
-		if ((prefix & flush_prefix) != prefix ||
+		/* Only remove the pending requests matching the flush prefix */
+		if (prefix != flush_prefix ||
 		    cm_req->cm_id == cm_ctx->active_cm_id) {
 			cur_node = next_node;
 			next_node = NULL;
@@ -329,12 +349,11 @@ cm_flush_pending_request(struct cnx_mgr *cm_ctx, uint32_t flush_prefix)
 		}
 
 		if (prefix == CONNECT_REQ_PREFIX) {
-			cm_connect_inform_os_if_connect_complete(cm_ctx,
-								 cm_req);
+			cm_handle_connect_flush(cm_ctx, cm_req);
 			cm_ctx->connect_count--;
 			cm_free_connect_req_mem(&cm_req->connect_req);
 		} else {
-			/* Todo:- fill disconnect rsp and inform OSIF */
+			cm_handle_disconnect_flush(cm_ctx, cm_req);
 			cm_ctx->disconnect_count--;
 		}
 		mlme_debug(CM_PREFIX_FMT,