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
Esse commit está contido em:
gaurank kathpalia
2020-10-08 10:46:28 +05:30
commit de snandini
commit 7d55cb743a
5 arquivos alterados com 263 adições e 43 exclusões

Ver arquivo

@@ -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;

Ver arquivo

@@ -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)
{

Ver arquivo

@@ -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 ****************/
/**

Ver arquivo

@@ -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",

Ver arquivo

@@ -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,