qcacmn: Add reassoc changes related to host roaming

Support reassoc request for host roaming

Change-Id: Iacb2950e314561c22ed1900de6522dd192cda12e
CRs-Fixed: 2845076
This commit is contained in:
Santosh Anbu
2020-12-17 20:00:42 +05:30
committed by snandini
父節點 bae1042f1c
當前提交 d788ea076d
共有 3 個文件被更改,包括 1270 次插入0 次删除

查看文件

@@ -0,0 +1,866 @@
/*
* Copyright (c) 2011-2021 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "wlan_cm_main.h"
#include "wlan_cm_roam_sm.h"
#include "wlan_cm_sm.h"
#include <wlan_mlme_cmn.h>
#include "wlan_cm_main_api.h"
#include <wlan_scan_api.h>
#include <wlan_serialization_api.h>
#include <wlan_utility.h>
#include <wlan_cm_api.h>
static void
cm_fill_roam_fail_resp_from_cm_id(struct cnx_mgr *cm_ctx,
struct wlan_cm_roam_resp *resp,
wlan_cm_id cm_id,
enum wlan_cm_connect_fail_reason reason)
{
resp->reassoc_status = QDF_STATUS_E_FAILURE;
resp->cm_id = cm_id;
resp->vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
resp->reason = reason;
cm_fill_bss_info_in_roam_rsp_by_cm_id(cm_ctx, cm_id, resp);
}
static QDF_STATUS
cm_reassoc_fail_disconnect(struct wlan_objmgr_vdev *vdev,
enum wlan_cm_source source,
enum wlan_reason_code reason_code,
struct qdf_mac_addr *bssid)
{
struct cnx_mgr *cm_ctx;
struct cm_req *cm_req;
struct cm_disconnect_req *disconnect_req;
struct wlan_cm_disconnect_req req = {0};
QDF_STATUS status;
cm_ctx = cm_get_cm_ctx(vdev);
if (!cm_ctx)
return QDF_STATUS_E_INVAL;
/*
* This would be freed as part of removal from cm req list if adding
* to list is success after posting WLAN_CM_SM_EV_DISCONNECT_REQ.
*/
cm_req = qdf_mem_malloc(sizeof(*cm_req));
if (!cm_req)
return QDF_STATUS_E_NOMEM;
req.vdev_id = wlan_vdev_get_id(vdev);
req.source = source;
req.reason_code = reason_code;
if (bssid)
qdf_copy_macaddr(&req.bssid, bssid);
disconnect_req = &cm_req->discon_req;
disconnect_req->req = req;
status = cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_DISCONNECT_REQ,
sizeof(*disconnect_req),
disconnect_req);
if (QDF_IS_STATUS_ERROR(status))
qdf_mem_free(cm_req);
return status;
}
QDF_STATUS
cm_send_reassoc_start_fail(struct cnx_mgr *cm_ctx,
wlan_cm_id cm_id,
enum wlan_cm_connect_fail_reason reason)
{
struct wlan_cm_roam_resp *resp;
QDF_STATUS status;
resp = qdf_mem_malloc(sizeof(*resp));
if (!resp)
return QDF_STATUS_E_NOMEM;
cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id, reason);
status = cm_sm_deliver_event(cm_ctx->vdev,
WLAN_CM_SM_EV_REASSOC_FAILURE,
sizeof(*resp), resp);
if (QDF_IS_STATUS_ERROR(status))
cm_reassoc_complete(cm_ctx, resp);
qdf_mem_free(resp);
return status;
}
static void cm_connect_prepare_scan_filter_for_roam(
struct wlan_objmgr_pdev *pdev,
struct cnx_mgr *cm_ctx, struct cm_roam_req *cm_req,
struct scan_filter *filter, bool security_valid_for_6ghz)
{
uint16_t rsn_caps;
struct wlan_objmgr_vdev *vdev = cm_ctx->vdev;
if (!qdf_is_macaddr_zero(&cm_req->req.bssid)) {
filter->num_of_bssid = 1;
qdf_copy_macaddr(&filter->bssid_list[0], &cm_req->req.bssid);
}
filter->num_of_ssid = 1;
wlan_vdev_mlme_get_ssid(vdev, filter->ssid_list[0].ssid,
&filter->ssid_list[0].length);
if (cm_req->req.chan_freq) {
filter->num_of_channels = 1;
filter->chan_freq_list[0] = cm_req->req.chan_freq;
}
/* Security is not valid for 6Ghz so ignore 6Ghz APs */
if (!security_valid_for_6ghz)
filter->ignore_6ghz_channel = true;
filter->authmodeset =
wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_AUTH_MODE);
filter->ucastcipherset =
wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_UCAST_CIPHER);
filter->mcastcipherset =
wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_MCAST_CIPHER);
filter->key_mgmt =
wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_KEY_MGMT);
filter->mgmtcipherset =
wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_MGMT_CIPHER);
if (!QDF_HAS_PARAM(filter->authmodeset, WLAN_CRYPTO_AUTH_WAPI) &&
!QDF_HAS_PARAM(filter->authmodeset, WLAN_CRYPTO_AUTH_RSNA) &&
!QDF_HAS_PARAM(filter->authmodeset, WLAN_CRYPTO_AUTH_WPA)) {
filter->ignore_auth_enc_type = 1;
}
rsn_caps =
wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_RSN_CAP);
if (rsn_caps & WLAN_CRYPTO_RSN_CAP_MFP_REQUIRED)
filter->pmf_cap = WLAN_PMF_REQUIRED;
else if (rsn_caps & WLAN_CRYPTO_RSN_CAP_MFP_ENABLED)
filter->pmf_cap = WLAN_PMF_CAPABLE;
else
filter->pmf_cap = WLAN_PMF_DISABLED;
}
QDF_STATUS cm_roam_get_candidates(
struct wlan_objmgr_pdev *pdev,
struct cnx_mgr *cm_ctx,
struct cm_roam_req *cm_req)
{
struct scan_filter *filter;
uint32_t num_bss = 0;
enum QDF_OPMODE op_mode;
qdf_list_t *candidate_list;
uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
qdf_list_node_t *cur_node = NULL;
bool security_valid_for_6ghz = true;
filter = qdf_mem_malloc(sizeof(*filter));
if (!filter)
return QDF_STATUS_E_NOMEM;
cm_connect_prepare_scan_filter_for_roam(pdev, cm_ctx, cm_req, filter,
security_valid_for_6ghz);
candidate_list = wlan_scan_get_result(pdev, filter);
if (candidate_list) {
num_bss = qdf_list_size(candidate_list);
mlme_debug(CM_PREFIX_FMT "num_entries found %d",
CM_PREFIX_REF(vdev_id, cm_req->cm_id), num_bss);
}
op_mode = wlan_vdev_mlme_get_opmode(cm_ctx->vdev);
if (num_bss && op_mode == QDF_STA_MODE)
cm_calculate_scores(pdev, filter, candidate_list);
qdf_mem_free(filter);
if (!candidate_list || !qdf_list_size(candidate_list)) {
if (candidate_list)
wlan_scan_purge_results(candidate_list);
mlme_info(CM_PREFIX_FMT "no valid candidate found, num_bss %d",
CM_PREFIX_REF(vdev_id, cm_req->cm_id), num_bss);
cm_req->candidate_list = NULL;
return QDF_STATUS_E_EMPTY;
}
qdf_list_peek_front(candidate_list, &cur_node);
cm_req->candidate_list = candidate_list;
cm_req->cur_candidate = qdf_container_of(cur_node,
struct scan_cache_node,
node);
return QDF_STATUS_SUCCESS;
}
QDF_STATUS cm_start_roam_req(struct cnx_mgr *cm_ctx,
struct cm_req *cm_req)
{
cm_req->roam_req.cm_id =
cm_get_cm_id(cm_ctx, cm_req->roam_req.req.source);
cm_req->cm_id = cm_req->roam_req.cm_id;
cm_req->roam_req.req.vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
cm_add_req_to_list_and_indicate_osif(cm_ctx, cm_req,
cm_req->roam_req.req.source);
cm_sm_transition_to(cm_ctx, WLAN_CM_SS_PREAUTH);
return cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_ROAM_START,
sizeof(*cm_req), cm_req);
}
QDF_STATUS cm_host_roam_start_req(struct cnx_mgr *cm_ctx,
struct cm_req *cm_req)
{
QDF_STATUS status;
struct wlan_objmgr_pdev *pdev;
enum wlan_cm_connect_fail_reason reason = CM_GENERIC_FAILURE;
uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
mlme_cm_roam_start_ind(cm_ctx->vdev, &cm_req->roam_req.req);
pdev = wlan_vdev_get_pdev(cm_ctx->vdev);
if (!pdev) {
mlme_err(CM_PREFIX_FMT "Failed to find pdev",
CM_PREFIX_REF(vdev_id, cm_req->cm_id));
reason = CM_GENERIC_FAILURE;
goto roam_err;
}
status = cm_roam_get_candidates(pdev, cm_ctx,
&cm_req->roam_req);
if (QDF_IS_STATUS_ERROR(status)) {
reason = CM_NO_CANDIDATE_FOUND;
goto roam_err;
}
status = cm_sm_deliver_event_sync(cm_ctx, WLAN_CM_SM_EV_START_REASSOC,
sizeof(cm_req->roam_req),
&cm_req->roam_req);
if (QDF_IS_STATUS_SUCCESS(status))
return status;
roam_err:
return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id, reason);
}
bool cm_roam_resp_cmid_match_list_head(struct cnx_mgr *cm_ctx,
struct wlan_cm_roam_resp *resp)
{
return cm_check_cmid_match_list_head(cm_ctx, &resp->cm_id);
}
static QDF_STATUS
cm_inform_if_mgr_reassoc_complete(struct wlan_objmgr_vdev *vdev,
QDF_STATUS status)
{
return QDF_STATUS_SUCCESS;
}
static inline QDF_STATUS
cm_inform_blm_reassoc_complete(struct wlan_objmgr_vdev *vdev,
struct wlan_cm_roam_resp *resp)
{
return QDF_STATUS_SUCCESS;
}
QDF_STATUS cm_reassoc_active(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id)
{
struct cm_req *cm_req;
struct wlan_cm_vdev_discon_req req;
cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id);
if (!cm_req)
return QDF_STATUS_E_INVAL;
cm_ctx->active_cm_id = *cm_id;
qdf_mem_zero(&req, sizeof(req));
req.cm_id = *cm_id;
req.req.vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
req.req.source = CM_ROAM_DISCONNECT;
wlan_vdev_get_bss_peer_mac(cm_ctx->vdev, &req.req.bssid);
return mlme_cm_disconnect_req(cm_ctx->vdev, &req);
}
QDF_STATUS cm_reassoc_disconnect_complete(struct cnx_mgr *cm_ctx,
struct wlan_cm_discon_rsp *resp)
{
QDF_STATUS status;
struct cm_req *cm_req;
struct qdf_mac_addr *bssid;
struct cm_disconnect_req discon_req;
uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
wlan_cm_id cm_id = cm_ctx->active_cm_id;
cm_req = cm_get_req_by_cm_id(cm_ctx, cm_id);
if (!cm_req)
return QDF_STATUS_E_INVAL;
qdf_mem_zero(&discon_req, sizeof(discon_req));
discon_req.cm_id = cm_id;
discon_req.req.vdev_id = vdev_id;
qdf_copy_macaddr(&discon_req.req.bssid,
&resp->req.req.bssid);
cm_update_scan_mlme_on_disconnect(cm_ctx->vdev, &discon_req);
mlme_cm_disconnect_complete_ind(cm_ctx->vdev, resp);
bssid = &cm_req->roam_req.cur_candidate->entry->bssid;
status = mlme_cm_bss_peer_create_req(cm_ctx->vdev, bssid);
if (QDF_IS_STATUS_ERROR(status)) {
mlme_err(CM_PREFIX_FMT "Peer create request failed",
CM_PREFIX_REF(vdev_id, cm_id));
status = cm_send_reassoc_start_fail(cm_ctx, cm_id,
CM_PEER_CREATE_FAILED);
}
return status;
}
QDF_STATUS
cm_resume_reassoc_after_peer_create(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id)
{
struct wlan_cm_vdev_reassoc_req req;
struct cm_req *cm_req;
QDF_STATUS status;
cm_req = cm_get_req_by_cm_id(cm_ctx, *cm_id);
if (!cm_req)
return QDF_STATUS_E_FAILURE;
req.vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
req.cm_id = *cm_id;
req.bss = cm_req->roam_req.cur_candidate;
status = mlme_cm_reassoc_req(cm_ctx->vdev, &req);
if (QDF_IS_STATUS_ERROR(status)) {
mlme_err(CM_PREFIX_FMT "Reassoc request failed",
CM_PREFIX_REF(req.vdev_id, req.cm_id));
mlme_cm_bss_peer_delete_req(cm_ctx->vdev);
status = cm_send_reassoc_start_fail(cm_ctx, *cm_id,
CM_JOIN_FAILED);
}
return status;
}
static void
cm_update_scan_db_on_reassoc_success(struct cnx_mgr *cm_ctx,
struct wlan_cm_roam_resp *resp)
{
struct element_info *bcn_probe_rsp;
struct cm_req *cm_req;
int32_t rssi;
if (!cm_is_vdev_connected(cm_ctx->vdev))
return;
cm_req = cm_get_req_by_cm_id(cm_ctx, resp->cm_id);
if (!cm_req)
return;
if (!cm_req->roam_req.cur_candidate)
return;
/*
* Get beacon or probe resp from connect response, and if not present
* use cur candidate to get beacon or probe resp
*/
if (resp->connect_ies.bcn_probe_rsp.ptr)
bcn_probe_rsp = &resp->connect_ies.bcn_probe_rsp;
else
bcn_probe_rsp =
&cm_req->roam_req.cur_candidate->entry->raw_frame;
rssi = cm_req->roam_req.cur_candidate->entry->rssi_raw;
cm_inform_bcn_probe(cm_ctx, bcn_probe_rsp->ptr, bcn_probe_rsp->len,
resp->freq, rssi, resp->cm_id);
}
inline void cm_set_fils_wep_key_on_reassoc(struct cnx_mgr *cm_ctx,
struct wlan_cm_roam_resp *resp)
{
}
QDF_STATUS cm_reassoc_complete(struct cnx_mgr *cm_ctx,
struct wlan_cm_roam_resp *resp)
{
enum wlan_cm_sm_state sm_state;
struct bss_info bss_info;
struct mlme_info mlme_info;
struct wlan_objmgr_vdev *vdev = cm_ctx->vdev;
/*
* If the entry is not present in the list, it must have been cleared
* already.
*/
if (!cm_get_req_by_cm_id(cm_ctx, resp->cm_id))
return QDF_STATUS_SUCCESS;
sm_state = cm_get_state(cm_ctx);
if (QDF_IS_STATUS_ERROR(resp->reassoc_status))
goto reassoc_fail;
if (QDF_IS_STATUS_SUCCESS(resp->reassoc_status) &&
sm_state == WLAN_CM_S_CONNECTED) {
cm_update_scan_db_on_reassoc_success(cm_ctx, resp);
cm_set_fils_wep_key_on_reassoc(cm_ctx, resp);
}
mlme_cm_reassoc_complete_ind(vdev, resp);
mlme_cm_osif_reassoc_complete(vdev, resp);
cm_inform_if_mgr_reassoc_complete(vdev, resp->reassoc_status);
cm_inform_blm_reassoc_complete(vdev, resp);
reassoc_fail:
/* Update scan entry in case connect is success or fails with bssid */
if (!qdf_is_macaddr_zero(&resp->bssid)) {
if (QDF_IS_STATUS_SUCCESS(resp->reassoc_status))
mlme_info.assoc_state = SCAN_ENTRY_CON_STATE_ASSOC;
else
mlme_info.assoc_state = SCAN_ENTRY_CON_STATE_NONE;
qdf_copy_macaddr(&bss_info.bssid, &resp->bssid);
bss_info.freq = resp->freq;
bss_info.ssid.length = resp->ssid.length;
qdf_mem_copy(&bss_info.ssid.ssid, resp->ssid.ssid,
bss_info.ssid.length);
wlan_scan_update_mlme_by_bssinfo(
wlan_vdev_get_pdev(vdev),
&bss_info, &mlme_info);
}
mlme_debug(CM_PREFIX_FMT,
CM_PREFIX_REF(wlan_vdev_get_id(vdev),
resp->cm_id));
cm_remove_cmd(cm_ctx, resp->cm_id);
/*
* If roaming fails and conn_sm is in ROAMING state, then
* initiate disconnect to cleanup and move conn_sm to INIT state
*/
if (QDF_IS_STATUS_ERROR(resp->reassoc_status) &&
sm_state == WLAN_CM_S_ROAMING) {
cm_reassoc_fail_disconnect(cm_ctx->vdev, CM_ROAM_DISCONNECT,
REASON_UNSPEC_FAILURE,
&resp->bssid);
}
return QDF_STATUS_SUCCESS;
}
void
cm_reassoc_handle_event_post_fail(struct cnx_mgr *cm_ctx, wlan_cm_id cm_id)
{
struct wlan_cm_roam_resp *resp;
resp = qdf_mem_malloc(sizeof(*resp));
if (!resp)
return;
cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id,
CM_GENERIC_FAILURE);
cm_reassoc_complete(cm_ctx, resp);
qdf_mem_free(resp);
}
static QDF_STATUS cm_reassoc_cmd_timeout(struct cnx_mgr *cm_ctx,
wlan_cm_id cm_id)
{
struct wlan_cm_roam_resp *resp;
QDF_STATUS status;
resp = qdf_mem_malloc(sizeof(*resp));
if (!resp)
return QDF_STATUS_E_NOMEM;
cm_fill_roam_fail_resp_from_cm_id(cm_ctx, resp, cm_id, CM_SER_TIMEOUT);
status = cm_sm_deliver_event(cm_ctx->vdev,
WLAN_CM_SM_EV_REASSOC_FAILURE,
sizeof(*resp), resp);
if (QDF_IS_STATUS_ERROR(status))
cm_reassoc_complete(cm_ctx, resp);
qdf_mem_free(resp);
return status;
}
#ifdef WLAN_CM_USE_SPINLOCK
static QDF_STATUS cm_activate_reassoc_req_flush_cb(struct scheduler_msg *msg)
{
struct wlan_serialization_command *cmd = msg->bodyptr;
if (!cmd || !cmd->vdev) {
mlme_err("Null input cmd:%pK", cmd);
return QDF_STATUS_E_INVAL;
}
wlan_objmgr_vdev_release_ref(cmd->vdev, WLAN_MLME_CM_ID);
return QDF_STATUS_SUCCESS;
}
static QDF_STATUS cm_activate_reassoc_req_sched_cb(struct scheduler_msg *msg)
{
struct wlan_serialization_command *cmd = msg->bodyptr;
struct wlan_objmgr_vdev *vdev;
struct cnx_mgr *cm_ctx;
QDF_STATUS ret = QDF_STATUS_E_FAILURE;
if (!cmd) {
mlme_err("cmd is null");
return QDF_STATUS_E_INVAL;
}
vdev = cmd->vdev;
if (!vdev) {
mlme_err("vdev is null");
return QDF_STATUS_E_INVAL;
}
cm_ctx = cm_get_cm_ctx(vdev);
if (!cm_ctx)
return QDF_STATUS_E_INVAL;
ret = cm_sm_deliver_event(vdev,
WLAN_CM_SM_EV_REASSOC_ACTIVE,
sizeof(wlan_cm_id),
&cmd->cmd_id);
/*
* Called from scheduler context hence posting failure
*/
if (QDF_IS_STATUS_ERROR(ret)) {
mlme_err(CM_PREFIX_FMT "Activation failed for cmd:%d",
CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id),
cmd->cmd_type);
cm_reassoc_handle_event_post_fail(cm_ctx, cmd->cmd_id);
}
wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
return ret;
}
static QDF_STATUS
cm_activate_reassoc_req(struct wlan_serialization_command *cmd)
{
struct wlan_objmgr_vdev *vdev = cmd->vdev;
struct scheduler_msg msg = {0};
QDF_STATUS ret;
msg.bodyptr = cmd;
msg.callback = cm_activate_reassoc_req_sched_cb;
msg.flush_callback = cm_activate_reassoc_req_flush_cb;
ret = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_MLME_CM_ID);
if (QDF_IS_STATUS_ERROR(ret))
return ret;
ret = scheduler_post_message(QDF_MODULE_ID_MLME,
QDF_MODULE_ID_MLME,
QDF_MODULE_ID_MLME, &msg);
if (QDF_IS_STATUS_ERROR(ret)) {
mlme_err(CM_PREFIX_FMT "Failed to post scheduler_msg",
CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id));
wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
return ret;
}
mlme_debug(CM_PREFIX_FMT "Cmd act in sched cmd type:%d",
CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id),
cmd->cmd_type);
return ret;
}
#else
static QDF_STATUS
cm_activate_reassoc_req(struct wlan_serialization_command *cmd)
{
return cm_sm_deliver_event(cmd->vdev,
WLAN_CM_SM_EV_REASSOC_ACTIVE,
sizeof(wlan_cm_id),
&cmd->cmd_id);
}
#endif
static QDF_STATUS
cm_ser_reassoc_cb(struct wlan_serialization_command *cmd,
enum wlan_serialization_cb_reason reason)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
struct wlan_objmgr_vdev *vdev;
struct cnx_mgr *cm_ctx;
if (!cmd) {
mlme_err("cmd is NULL, reason: %d", reason);
QDF_ASSERT(0);
return QDF_STATUS_E_NULL_VALUE;
}
vdev = cmd->vdev;
cm_ctx = cm_get_cm_ctx(vdev);
if (!cm_ctx)
return QDF_STATUS_E_NULL_VALUE;
switch (reason) {
case WLAN_SER_CB_ACTIVATE_CMD:
/*
* For pending to active reason, use async api to take lock.
* For direct activation use sync api to avoid taking lock
* as lock is already acquired by the requester.
*/
if (cmd->activation_reason == SER_PENDING_TO_ACTIVE)
status = cm_activate_reassoc_req(cmd);
else
status = cm_sm_deliver_event_sync(
cm_ctx, WLAN_CM_SM_EV_REASSOC_ACTIVE,
sizeof(wlan_cm_id), &cmd->cmd_id);
if (QDF_IS_STATUS_SUCCESS(status))
break;
/*
* Handle failure if posting fails, i.e. the SM state has
* changed or head cm_id doesn't match the active cm_id.
* connect active should be handled only in JOIN_PENDING. If
* new command has been received connect activation should be
* aborted from here with connect req cleanup.
*/
cm_reassoc_handle_event_post_fail(cm_ctx, cmd->cmd_id);
break;
case WLAN_SER_CB_CANCEL_CMD:
/* command removed from pending list. */
break;
case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT:
mlme_err(CM_PREFIX_FMT "Active command timeout",
CM_PREFIX_REF(wlan_vdev_get_id(vdev), cmd->cmd_id));
QDF_ASSERT(0);
cm_reassoc_cmd_timeout(cm_ctx, cmd->cmd_id);
break;
case WLAN_SER_CB_RELEASE_MEM_CMD:
cm_reset_active_cm_id(vdev, cmd->cmd_id);
wlan_objmgr_vdev_release_ref(vdev, WLAN_MLME_CM_ID);
break;
default:
QDF_ASSERT(0);
status = QDF_STATUS_E_INVAL;
break;
}
return status;
}
#define REASSOC_TIMEOUT 10000
static QDF_STATUS cm_ser_reassoc_req(struct cnx_mgr *cm_ctx,
struct cm_roam_req *cm_req)
{
struct wlan_serialization_command cmd = {0, };
enum wlan_serialization_status ser_cmd_status;
QDF_STATUS status;
uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
status = wlan_objmgr_vdev_try_get_ref(cm_ctx->vdev, WLAN_MLME_CM_ID);
if (QDF_IS_STATUS_ERROR(status)) {
mlme_err(CM_PREFIX_FMT "unable to get reference",
CM_PREFIX_REF(vdev_id, cm_req->cm_id));
return status;
}
cmd.cmd_type = WLAN_SER_CMD_VDEV_REASSOC;
cmd.cmd_id = cm_req->cm_id;
cmd.cmd_cb = cm_ser_reassoc_cb;
cmd.source = WLAN_UMAC_COMP_MLME;
cmd.is_high_priority = false;
cmd.cmd_timeout_duration = REASSOC_TIMEOUT;
cmd.vdev = cm_ctx->vdev;
cmd.is_blocking = cm_ser_get_blocking_cmd();
ser_cmd_status = wlan_serialization_request(&cmd);
switch (ser_cmd_status) {
case WLAN_SER_CMD_PENDING:
/* command moved to pending list.Do nothing */
break;
case WLAN_SER_CMD_ACTIVE:
/* command moved to active list. Do nothing */
break;
default:
mlme_err(CM_PREFIX_FMT "ser cmd status %d",
CM_PREFIX_REF(vdev_id, cm_req->cm_id), ser_cmd_status);
wlan_objmgr_vdev_release_ref(cm_ctx->vdev, WLAN_MLME_CM_ID);
return QDF_STATUS_E_FAILURE;
}
return QDF_STATUS_SUCCESS;
}
QDF_STATUS cm_reassoc_start(struct cnx_mgr *cm_ctx,
struct cm_roam_req *cm_req)
{
QDF_STATUS status;
uint8_t vdev_id = wlan_vdev_get_id(cm_ctx->vdev);
status = cm_ser_reassoc_req(cm_ctx, cm_req);
if (QDF_IS_STATUS_ERROR(status)) {
mlme_err(CM_PREFIX_FMT "Serialization of reassoc failed",
CM_PREFIX_REF(vdev_id, cm_req->cm_id));
return cm_send_reassoc_start_fail(cm_ctx, cm_req->cm_id,
CM_SER_FAILURE);
}
return status;
}
QDF_STATUS cm_reassoc_rsp(struct wlan_objmgr_vdev *vdev,
struct wlan_cm_roam_resp *resp)
{
struct cnx_mgr *cm_ctx;
QDF_STATUS qdf_status;
wlan_cm_id cm_id;
uint32_t prefix;
cm_ctx = cm_get_cm_ctx(vdev);
if (!cm_ctx)
return QDF_STATUS_E_INVAL;
cm_id = cm_ctx->active_cm_id;
prefix = CM_ID_GET_PREFIX(cm_id);
if (prefix != ROAM_REQ_PREFIX ||
cm_id != resp->cm_id) {
mlme_err(CM_PREFIX_FMT " Active cm_id 0x%x is different",
CM_PREFIX_REF(wlan_vdev_get_id(vdev), resp->cm_id),
cm_id);
qdf_status = QDF_STATUS_E_FAILURE;
goto post_err;
}
if (QDF_IS_STATUS_SUCCESS(resp->reassoc_status)) {
/*
* On successful connection to sae single pmk AP,
* clear all the single pmk AP.
*/
if (cm_is_cm_id_current_candidate_single_pmk(cm_ctx, cm_id))
wlan_crypto_selective_clear_sae_single_pmk_entries(
vdev, &resp->bssid);
qdf_status = cm_sm_deliver_event(vdev,
WLAN_CM_SM_EV_REASSOC_DONE,
sizeof(*resp), resp);
if (QDF_IS_STATUS_SUCCESS(qdf_status))
return qdf_status;
}
post_err:
cm_reassoc_complete(cm_ctx, resp);
return qdf_status;
}
QDF_STATUS cm_roam_bss_peer_create_rsp(struct wlan_objmgr_vdev *vdev,
QDF_STATUS status,
struct qdf_mac_addr *peer_mac)
{
struct cnx_mgr *cm_ctx;
QDF_STATUS qdf_status;
wlan_cm_id cm_id;
uint32_t prefix;
struct cm_req *cm_req;
cm_ctx = cm_get_cm_ctx(vdev);
if (!cm_ctx)
return QDF_STATUS_E_INVAL;
cm_id = cm_ctx->active_cm_id;
prefix = CM_ID_GET_PREFIX(cm_id);
if (prefix != ROAM_REQ_PREFIX) {
mlme_err(CM_PREFIX_FMT "Active req is not roam req",
CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev), cm_id));
mlme_cm_bss_peer_delete_req(vdev);
return QDF_STATUS_E_INVAL;
}
if (QDF_IS_STATUS_ERROR(status)) {
cm_req = cm_get_req_by_cm_id(cm_ctx, cm_id);
if (!cm_req)
return QDF_STATUS_E_INVAL;
return cm_send_reassoc_start_fail(
cm_ctx, cm_id,
CM_PEER_CREATE_FAILED);
}
qdf_status = cm_sm_deliver_event(
vdev, WLAN_CM_SM_EV_BSS_CREATE_PEER_SUCCESS,
sizeof(wlan_cm_id), &cm_id);
if (QDF_IS_STATUS_SUCCESS(qdf_status))
return qdf_status;
mlme_cm_bss_peer_delete_req(vdev);
cm_reassoc_handle_event_post_fail(cm_ctx, cm_id);
return qdf_status;
}
QDF_STATUS cm_roam_disconnect_rsp(struct wlan_objmgr_vdev *vdev,
struct wlan_cm_discon_rsp *resp)
{
struct cnx_mgr *cm_ctx;
QDF_STATUS qdf_status;
wlan_cm_id cm_id;
uint32_t prefix;
cm_ctx = cm_get_cm_ctx(vdev);
if (!cm_ctx)
return QDF_STATUS_E_INVAL;
cm_id = cm_ctx->active_cm_id;
prefix = CM_ID_GET_PREFIX(cm_id);
if (prefix != ROAM_REQ_PREFIX || cm_id != resp->req.cm_id) {
mlme_err(CM_PREFIX_FMT "Active cm_id 0x%x is different",
CM_PREFIX_REF(wlan_vdev_get_id(vdev), resp->req.cm_id),
cm_id);
qdf_status = QDF_STATUS_E_FAILURE;
goto disconnect_complete;
}
qdf_status =
cm_sm_deliver_event(vdev,
WLAN_CM_SM_EV_DISCONNECT_DONE,
sizeof(*resp), resp);
if (QDF_IS_STATUS_SUCCESS(qdf_status))
return qdf_status;
disconnect_complete:
cm_reassoc_handle_event_post_fail(cm_ctx, cm_id);
return qdf_status;
}
void cm_start_roam_invoke(struct cnx_mgr *cm_ctx)
{
}
void cm_fw_roam_start(struct cnx_mgr *cm_ctx)
{
}

查看文件

@@ -0,0 +1,236 @@
/*
* Copyright (c) 2011-2021 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC: wlan_cm_roam.h
*
* This header file maintain APIs required for connection mgr roam functions
*/
#ifndef __WLAN_CM_ROAM_H__
#define __WLAN_CM_ROAM_H__
/**
* cm_check_and_prepare_roam_req() - Initiate roam request
* @cm_ctx: connection manager context
* @connect_req: connection manager request
* @roam_req: Roam request
*
* Context: Can be called only while handling connection manager event
* ie holding state machine lock
*
* Return: QDF_STATUS
*/
QDF_STATUS
cm_check_and_prepare_roam_req(struct cnx_mgr *cm_ctx,
struct cm_connect_req *connect_req,
struct cm_req **roam_req);
/**
* cm_roam_bss_peer_create_rsp() - handle bss peer create response for roam
* @vdev: vdev
* @status: bss peer create status
* @peer_mac: peer mac
*
* Return: QDF status
*/
QDF_STATUS cm_roam_bss_peer_create_rsp(struct wlan_objmgr_vdev *vdev,
QDF_STATUS status,
struct qdf_mac_addr *peer_mac);
/**
* cm_reassoc_rsp() - Connection manager reassoc response
* @vdev: vdev pointer
* @resp: Reassoc response
*
* Return: QDF_STATUS
*/
QDF_STATUS cm_reassoc_rsp(struct wlan_objmgr_vdev *vdev,
struct wlan_cm_roam_resp *resp);
/**
* cm_roam_disconnect_rsp() - Connection manager api to post connect event
* @vdev: VDEV object
* @cm_discon_rsp: Disconnect response
*
* Context: Any context.
*
* Return: QDF_STATUS
*/
QDF_STATUS cm_roam_disconnect_rsp(struct wlan_objmgr_vdev *vdev,
struct wlan_cm_discon_rsp *resp);
/**
* cm_reassoc_complete() - This API would be called after reassoc complete
* request from the serialization.
* @cm_ctx: connection manager context
* @resp: Roam complete resp.
*
* This API would be called after roam completion resp from VDEV mgr
*
* Return: QDF status
*/
#ifdef WLAN_FEATURE_HOST_ROAM
QDF_STATUS cm_reassoc_complete(struct cnx_mgr *cm_ctx,
struct wlan_cm_roam_resp *resp);
#else
static inline QDF_STATUS cm_reassoc_complete(struct cnx_mgr *cm_ctx,
struct wlan_cm_roam_resp *resp)
{}
#endif
/**
* cm_free_roam_req_mem() - free croam req internal memory, to be called
* before cm_req is freed
* @roam_req: roam req
*
* Return: void
*/
void cm_free_roam_req_mem(struct cm_roam_req *roam_req);
/**
* cm_get_active_reassoc_req() - Get copy of active reassoc request
* @vdev: vdev pointer
* @req: pointer to the copy of the active reassoc request
* *
* Context: Should be called only in the conext of the
* cm request activation
*
* Return: true and reassoc req if any request is active
*/
bool cm_get_active_reassoc_req(struct wlan_objmgr_vdev *vdev,
struct wlan_cm_vdev_reassoc_req *req);
/**
* cm_start_roam_req() - Initiate roam request
* @cm_ctx: Connection manager context
* @cm_req: Struct containing the roam request
*
* Return: QDF_STATUS_SUCCESS if roam initiation delivers the event
* to connection state machine else error value.
*/
QDF_STATUS cm_start_roam_req(struct cnx_mgr *cm_ctx,
struct cm_req *cm_req);
/**
* cm_host_roam_start_req() - Start host roam request
* @cm_ctx: Connection manager context
* @cm_req: Struct containing the roam request
*
* Return: QDF_STATUS_SUCCESS on delivering the event
* to connection state machine else error value.
*/
QDF_STATUS cm_host_roam_start_req(struct cnx_mgr *cm_ctx,
struct cm_req *cm_req);
/**
* cm_start_roam_invoke() - Start roam request
* @cm_ctx: Connection manager context
*
* Return: void
*/
void cm_start_roam_invoke(struct cnx_mgr *cm_ctx);
/**
* cm_fw_roam_start() - Initiate FW roam request
* @cm_ctx: Connection manager context
*
* Return: void
*/
void cm_fw_roam_start(struct cnx_mgr *cm_ctx);
/**
* cm_reassoc_start() - This API will be called to initiate the reassoc
* process
* @cm_ctx: connection manager context
* @req: roam request.
*
* Return: QDF status
*/
QDF_STATUS cm_reassoc_start(struct cnx_mgr *cm_ctx, struct cm_roam_req *req);
/**
* cm_reassoc_active() - This API would be called after the reassoc
* request gets activated in serialization.
* @cm_ctx: connection manager context
* @cm_id: Connection mgr ID assigned to this reassoc request.
*
* Return: QDF status
*/
QDF_STATUS cm_reassoc_active(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id);
/**
* cm_reassoc_disconnect_complete() - This API would be called after
* disconnect complete due to reassoc request.
* @cm_ctx: connection manager context
* @resp: disconnection complete resp.
*
* This API would be called after disconnection completion resp from VDEV mgr
*
* Return: QDF status
*/
QDF_STATUS cm_reassoc_disconnect_complete(struct cnx_mgr *cm_ctx,
struct wlan_cm_discon_rsp *resp);
/**
* cm_resume_reassoc_after_peer_create() - Called after bss create rsp
* @cm_ctx: connection manager context
* @cm_id: Connection mgr ID assigned to this reassoc request.
*
* Return: QDF status
*/
QDF_STATUS
cm_resume_reassoc_after_peer_create(struct cnx_mgr *cm_ctx, wlan_cm_id *cm_id);
/**
* cm_roam_resp_cmid_match_list_head() - Check if resp cmid is same as list
* head
* @cm_ctx: connection manager context
* @resp: roam resp
*
* Return: bool
*/
bool cm_roam_resp_cmid_match_list_head(struct cnx_mgr *cm_ctx,
struct wlan_cm_roam_resp *resp);
/**
* cm_send_reassoc_start_fail() - initiate reassoc failure
* @cm_ctx: connection manager context
* @cm_id: active command id
* @reason: failure reason
*
* Return: QDF_STATUS
*/
QDF_STATUS
cm_send_reassoc_start_fail(struct cnx_mgr *cm_ctx,
wlan_cm_id cm_id,
enum wlan_cm_connect_fail_reason reason);
/**
* cm_fill_bss_info_in_roam_rsp_by_cm_id() - fill bss info for the cm id
* @cm_ctx: connection manager context
* @cm_id: cm id of connect/disconnect req
* @resp: resp to copy bss info like ssid/bssid and freq
*
* Return: Success if entry was found else failure
*/
QDF_STATUS
cm_fill_bss_info_in_roam_rsp_by_cm_id(struct cnx_mgr *cm_ctx,
wlan_cm_id cm_id,
struct wlan_cm_roam_resp *resp);
#endif /* __WLAN_CM_ROAM_H__ */

查看文件

@@ -0,0 +1,168 @@
/*
* Copyright (c) 2011-2021 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC: Implements general Roam utils for connection manager
*/
#include "wlan_cm_main.h"
#include "wlan_cm_roam_sm.h"
#include "wlan_cm_sm.h"
#include "wlan_cm_main_api.h"
#include "wlan_cm_roam.h"
#include <wlan_scan_api.h>
QDF_STATUS cm_check_and_prepare_roam_req(struct cnx_mgr *cm_ctx,
struct cm_connect_req *connect_req,
struct cm_req **roam_req)
{
QDF_STATUS status;
struct wlan_cm_connect_req *req;
struct qdf_mac_addr bssid;
struct wlan_ssid ssid;
struct cm_req *cm_req, *req_ptr;
cm_req = qdf_container_of(connect_req, struct cm_req, connect_req);
req = &connect_req->req;
/*
* Reject re-assoc unless freq along with prev bssid and one
* of bssid or bssid hint is present.
*/
if (!req->chan_freq || qdf_is_macaddr_zero(&req->prev_bssid) ||
(qdf_is_macaddr_zero(&req->bssid) &&
qdf_is_macaddr_zero(&req->bssid_hint)))
return QDF_STATUS_E_FAILURE;
wlan_vdev_get_bss_peer_mac(cm_ctx->vdev, &bssid);
/* Reject re-assoc unless prev_bssid matches the current BSSID. */
if (!qdf_is_macaddr_equal(&req->prev_bssid, &bssid))
return QDF_STATUS_E_FAILURE;
status = wlan_vdev_mlme_get_ssid(cm_ctx->vdev, ssid.ssid, &ssid.length);
if (QDF_IS_STATUS_ERROR(status)) {
mlme_err("failed to get ssid");
return QDF_STATUS_E_FAILURE;
}
/* Reject re-assoc unless ssid matches. */
if (ssid.length != req->ssid.length ||
qdf_mem_cmp(ssid.ssid, req->ssid.ssid, ssid.length))
return QDF_STATUS_E_FAILURE;
/* fill roam_req for roaming and free cm_req */
*roam_req = qdf_mem_malloc(sizeof(**roam_req));
if (!*roam_req)
return QDF_STATUS_E_NOMEM;
req_ptr = *roam_req;
if (!qdf_is_macaddr_zero(&req->bssid))
qdf_copy_macaddr(&req_ptr->roam_req.req.bssid, &req->bssid);
else
qdf_copy_macaddr(&req_ptr->roam_req.req.bssid,
&req->bssid_hint);
qdf_copy_macaddr(&req_ptr->roam_req.req.prev_bssid, &req->prev_bssid);
req_ptr->roam_req.req.chan_freq = req->chan_freq;
req_ptr->roam_req.req.source = CM_ROAMING_HOST;
/* Free the connect req, as reassoc is tried */
cm_free_connect_req_mem(connect_req);
qdf_mem_free(cm_req);
return QDF_STATUS_SUCCESS;
}
#ifdef WLAN_FEATURE_HOST_ROAM
bool cm_get_active_reassoc_req(struct wlan_objmgr_vdev *vdev,
struct wlan_cm_vdev_reassoc_req *req)
{
struct cnx_mgr *cm_ctx;
qdf_list_node_t *cur_node = NULL, *next_node = NULL;
struct cm_req *cm_req = NULL;
bool status = false;
uint32_t cm_id_prefix;
cm_ctx = cm_get_cm_ctx(vdev);
cm_req_lock_acquire(cm_ctx);
qdf_list_peek_front(&cm_ctx->req_list, &cur_node);
while (cur_node) {
qdf_list_peek_next(&cm_ctx->req_list, cur_node, &next_node);
cm_req = qdf_container_of(cur_node, struct cm_req, node);
cm_id_prefix = CM_ID_GET_PREFIX((cm_req->cm_id));
if (cm_req->cm_id == cm_ctx->active_cm_id &&
cm_id_prefix == ROAM_REQ_PREFIX) {
req->vdev_id = wlan_vdev_get_id(vdev);
req->cm_id = cm_req->roam_req.cm_id;
qdf_copy_macaddr(&req->prev_bssid,
&cm_req->roam_req.req.prev_bssid);
req->bss = cm_req->roam_req.cur_candidate;
status = true;
cm_req_lock_release(cm_ctx);
return status;
}
cur_node = next_node;
next_node = NULL;
}
cm_req_lock_release(cm_ctx);
return status;
}
#endif
QDF_STATUS
cm_fill_bss_info_in_roam_rsp_by_cm_id(struct cnx_mgr *cm_ctx,
wlan_cm_id cm_id,
struct wlan_cm_roam_resp *resp)
{
qdf_list_node_t *cur_node = NULL, *next_node = NULL;
struct cm_req *cm_req;
uint32_t prefix = CM_ID_GET_PREFIX(cm_id);
struct wlan_cm_roam_req *req;
if (prefix != ROAM_REQ_PREFIX)
return QDF_STATUS_E_INVAL;
cm_req_lock_acquire(cm_ctx);
qdf_list_peek_front(&cm_ctx->req_list, &cur_node);
while (cur_node) {
qdf_list_peek_next(&cm_ctx->req_list, cur_node, &next_node);
cm_req = qdf_container_of(cur_node, struct cm_req, node);
if (cm_req->cm_id == cm_id) {
req = &cm_req->roam_req.req;
resp->freq = req->chan_freq;
wlan_vdev_mlme_get_ssid(cm_ctx->vdev, resp->ssid.ssid,
&resp->ssid.length);
if (!qdf_is_macaddr_zero(&req->bssid))
qdf_copy_macaddr(&resp->bssid, &req->bssid);
cm_req_lock_release(cm_ctx);
return QDF_STATUS_SUCCESS;
}
cur_node = next_node;
next_node = NULL;
}
cm_req_lock_release(cm_ctx);
return QDF_STATUS_E_FAILURE;
}