| /* |
| * Copyright (c) 2017-2018 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_tdls_cmds_process.c |
| * |
| * TDLS north bound commands implementation |
| */ |
| #include <wlan_reg_services_api.h> |
| #include <wlan_serialization_api.h> |
| #include "wlan_tdls_main.h" |
| #include "wlan_tdls_peer.h" |
| #include "wlan_tdls_ct.h" |
| #include "wlan_tdls_mgmt.h" |
| #include "wlan_tdls_cmds_process.h" |
| #include "wlan_tdls_tgt_api.h" |
| #include "wlan_policy_mgr_api.h" |
| |
| static uint16_t tdls_get_connected_peer(struct tdls_soc_priv_obj *soc_obj) |
| { |
| return soc_obj->connected_peer_count; |
| } |
| |
| /** |
| * tdls_decrement_peer_count() - decrement connected TDLS peer counter |
| * @soc_obj: TDLS soc object |
| * |
| * Used in scheduler thread context, no lock needed. |
| * |
| * Return: None. |
| */ |
| void tdls_decrement_peer_count(struct tdls_soc_priv_obj *soc_obj) |
| { |
| if (soc_obj->connected_peer_count) |
| soc_obj->connected_peer_count--; |
| |
| tdls_debug("Connected peer count %d", soc_obj->connected_peer_count); |
| } |
| |
| /** |
| * tdls_increment_peer_count() - increment connected TDLS peer counter |
| * @soc_obj: TDLS soc object |
| * |
| * Used in scheduler thread context, no lock needed. |
| * |
| * Return: None. |
| */ |
| static void tdls_increment_peer_count(struct tdls_soc_priv_obj *soc_obj) |
| { |
| soc_obj->connected_peer_count++; |
| tdls_debug("Connected peer count %d", soc_obj->connected_peer_count); |
| } |
| |
| /** |
| * tdls_validate_current_mode() - check current TDL mode |
| * @soc_obj: TDLS soc object |
| * |
| * Return: QDF_STATUS_SUCCESS if TDLS enabled, other for disabled |
| */ |
| static QDF_STATUS tdls_validate_current_mode(struct tdls_soc_priv_obj *soc_obj) |
| { |
| if (soc_obj->tdls_current_mode == TDLS_SUPPORT_DISABLED || |
| soc_obj->tdls_current_mode == TDLS_SUPPORT_SUSPENDED) { |
| tdls_err("TDLS mode disabled OR not enabled, current mode %d", |
| soc_obj->tdls_current_mode); |
| return QDF_STATUS_E_NOSUPPORT; |
| } |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| static char *tdls_get_ser_cmd_str(enum wlan_serialization_cmd_type type) |
| { |
| switch (type) { |
| case WLAN_SER_CMD_TDLS_ADD_PEER: |
| return "TDLS_ADD_PEER_CMD"; |
| case WLAN_SER_CMD_TDLS_DEL_PEER: |
| return "TDLS_DEL_PEER_CMD"; |
| case WLAN_SER_CMD_TDLS_SEND_MGMT: |
| return "TDLS_SEND_MGMT_CMD"; |
| default: |
| return "UNKNOWN"; |
| } |
| } |
| |
| void |
| tdls_release_serialization_command(struct wlan_objmgr_vdev *vdev, |
| enum wlan_serialization_cmd_type type) |
| { |
| struct wlan_serialization_queued_cmd_info cmd = {0}; |
| |
| cmd.cmd_type = type; |
| cmd.cmd_id = 0; |
| cmd.vdev = vdev; |
| |
| tdls_debug("release %s", tdls_get_ser_cmd_str(type)); |
| /* Inform serialization for command completion */ |
| wlan_serialization_remove_cmd(&cmd); |
| } |
| |
| /** |
| * tdls_pe_add_peer() - send TDLS add peer request to PE |
| * @req: TDL add peer request |
| * |
| * Return: QDF_STATUS_SUCCESS for success; other values if failed |
| */ |
| static QDF_STATUS tdls_pe_add_peer(struct tdls_add_peer_request *req) |
| { |
| struct tdls_add_sta_req *addstareq; |
| struct wlan_objmgr_vdev *vdev; |
| struct wlan_objmgr_peer *peer; |
| struct tdls_soc_priv_obj *soc_obj; |
| struct scheduler_msg msg = {0,}; |
| QDF_STATUS status; |
| |
| addstareq = qdf_mem_malloc(sizeof(*addstareq)); |
| if (!addstareq) { |
| tdls_err("allocate failed"); |
| return QDF_STATUS_E_NOMEM; |
| } |
| vdev = req->vdev; |
| soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); |
| if (!soc_obj) { |
| tdls_err("NULL tdls soc object"); |
| status = QDF_STATUS_E_INVAL; |
| goto error; |
| } |
| |
| addstareq->tdls_oper = TDLS_OPER_ADD; |
| addstareq->transaction_id = 0; |
| |
| addstareq->session_id = wlan_vdev_get_id(vdev); |
| peer = wlan_vdev_get_bsspeer(vdev); |
| if (!peer) { |
| tdls_err("bss peer is NULL"); |
| status = QDF_STATUS_E_INVAL; |
| goto error; |
| } |
| status = wlan_objmgr_peer_try_get_ref(peer, WLAN_TDLS_NB_ID); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| tdls_err("can't get bss peer"); |
| goto error; |
| } |
| wlan_peer_obj_lock(peer); |
| qdf_mem_copy(addstareq->bssid.bytes, |
| wlan_peer_get_macaddr(peer), QDF_MAC_ADDR_SIZE); |
| wlan_peer_obj_unlock(peer); |
| wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_NB_ID); |
| qdf_mem_copy(addstareq->peermac.bytes, req->add_peer_req.peer_addr, |
| QDF_MAC_ADDR_SIZE); |
| |
| tdls_debug("for " QDF_MAC_ADDR_STR, |
| QDF_MAC_ADDR_ARRAY(addstareq->peermac.bytes)); |
| msg.type = soc_obj->tdls_add_sta_req; |
| msg.bodyptr = addstareq; |
| status = scheduler_post_message(QDF_MODULE_ID_TDLS, |
| QDF_MODULE_ID_PE, |
| QDF_MODULE_ID_PE, &msg); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| tdls_err("fail to post pe msg to add peer"); |
| goto error; |
| } |
| return status; |
| error: |
| qdf_mem_free(addstareq); |
| return status; |
| } |
| |
| /** |
| * tdls_pe_del_peer() - send TDLS delete peer request to PE |
| * @req: TDLS delete peer request |
| * |
| * Return: QDF_STATUS_SUCCESS for success; other values if failed |
| */ |
| QDF_STATUS tdls_pe_del_peer(struct tdls_del_peer_request *req) |
| { |
| struct tdls_del_sta_req *delstareq; |
| struct wlan_objmgr_vdev *vdev; |
| struct wlan_objmgr_peer *peer; |
| struct tdls_soc_priv_obj *soc_obj; |
| struct scheduler_msg msg = {0,}; |
| QDF_STATUS status; |
| |
| delstareq = qdf_mem_malloc(sizeof(*delstareq)); |
| if (!delstareq) { |
| tdls_err("allocate failed"); |
| return QDF_STATUS_E_NOMEM; |
| } |
| vdev = req->vdev; |
| soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); |
| if (!soc_obj) { |
| tdls_err("NULL tdls soc object"); |
| status = QDF_STATUS_E_INVAL; |
| goto error; |
| } |
| |
| delstareq->transaction_id = 0; |
| |
| delstareq->session_id = wlan_vdev_get_id(vdev); |
| peer = wlan_vdev_get_bsspeer(vdev); |
| if (!peer) { |
| tdls_err("bss peer is NULL"); |
| status = QDF_STATUS_E_INVAL; |
| goto error; |
| } |
| status = wlan_objmgr_peer_try_get_ref(peer, WLAN_TDLS_NB_ID); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| tdls_err("can't get bss peer"); |
| goto error; |
| } |
| wlan_peer_obj_lock(peer); |
| qdf_mem_copy(delstareq->bssid.bytes, |
| wlan_peer_get_macaddr(peer), QDF_MAC_ADDR_SIZE); |
| wlan_peer_obj_unlock(peer); |
| wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_NB_ID); |
| qdf_mem_copy(delstareq->peermac.bytes, req->del_peer_req.peer_addr, |
| QDF_MAC_ADDR_SIZE); |
| |
| tdls_debug("for " QDF_MAC_ADDR_STR, |
| QDF_MAC_ADDR_ARRAY(delstareq->peermac.bytes)); |
| msg.type = soc_obj->tdls_del_sta_req; |
| msg.bodyptr = delstareq; |
| status = scheduler_post_message(QDF_MODULE_ID_TDLS, |
| QDF_MODULE_ID_PE, |
| QDF_MODULE_ID_PE, &msg); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| tdls_err("fail to post pe msg to del peer"); |
| goto error; |
| } |
| return status; |
| error: |
| qdf_mem_free(delstareq); |
| return status; |
| } |
| |
| /** |
| * tdls_pe_update_peer() - send TDLS update peer request to PE |
| * @req: TDLS update peer request |
| * |
| * Return: QDF_STATUS_SUCCESS for success; other values if failed |
| */ |
| static QDF_STATUS tdls_pe_update_peer(struct tdls_update_peer_request *req) |
| { |
| struct tdls_add_sta_req *addstareq; |
| struct wlan_objmgr_vdev *vdev; |
| struct wlan_objmgr_peer *peer; |
| struct tdls_soc_priv_obj *soc_obj; |
| struct scheduler_msg msg = {0,}; |
| struct tdls_update_peer_params *update_peer; |
| QDF_STATUS status; |
| |
| addstareq = qdf_mem_malloc(sizeof(*addstareq)); |
| if (!addstareq) { |
| tdls_err("allocate failed"); |
| return QDF_STATUS_E_NOMEM; |
| } |
| vdev = req->vdev; |
| soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); |
| if (!soc_obj) { |
| tdls_err("NULL tdls soc object"); |
| status = QDF_STATUS_E_INVAL; |
| goto error; |
| } |
| update_peer = &req->update_peer_req; |
| |
| addstareq->tdls_oper = TDLS_OPER_UPDATE; |
| addstareq->transaction_id = 0; |
| |
| addstareq->session_id = wlan_vdev_get_id(vdev); |
| peer = wlan_vdev_get_bsspeer(vdev); |
| if (!peer) { |
| tdls_err("bss peer is NULL"); |
| status = QDF_STATUS_E_INVAL; |
| goto error; |
| } |
| status = wlan_objmgr_peer_try_get_ref(peer, WLAN_TDLS_NB_ID); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| tdls_err("can't get bss peer"); |
| goto error; |
| } |
| wlan_peer_obj_lock(peer); |
| qdf_mem_copy(addstareq->bssid.bytes, |
| wlan_peer_get_macaddr(peer), QDF_MAC_ADDR_SIZE); |
| wlan_peer_obj_unlock(peer); |
| wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_NB_ID); |
| qdf_mem_copy(addstareq->peermac.bytes, update_peer->peer_addr, |
| QDF_MAC_ADDR_SIZE); |
| addstareq->capability = update_peer->capability; |
| addstareq->uapsd_queues = update_peer->uapsd_queues; |
| addstareq->max_sp = update_peer->max_sp; |
| |
| qdf_mem_copy(addstareq->extn_capability, |
| update_peer->extn_capability, WLAN_MAC_MAX_EXTN_CAP); |
| addstareq->htcap_present = update_peer->htcap_present; |
| qdf_mem_copy(&addstareq->ht_cap, |
| &update_peer->ht_cap, |
| sizeof(update_peer->ht_cap)); |
| addstareq->vhtcap_present = update_peer->vhtcap_present; |
| qdf_mem_copy(&addstareq->vht_cap, |
| &update_peer->vht_cap, |
| sizeof(update_peer->vht_cap)); |
| addstareq->supported_rates_length = update_peer->supported_rates_len; |
| qdf_mem_copy(&addstareq->supported_rates, |
| update_peer->supported_rates, |
| update_peer->supported_rates_len); |
| tdls_debug("for " QDF_MAC_ADDR_STR, |
| QDF_MAC_ADDR_ARRAY(addstareq->peermac.bytes)); |
| |
| msg.type = soc_obj->tdls_add_sta_req; |
| msg.bodyptr = addstareq; |
| status = scheduler_post_message(QDF_MODULE_ID_TDLS, |
| QDF_MODULE_ID_PE, |
| QDF_MODULE_ID_PE, &msg); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| tdls_err("fail to post pe msg to update peer"); |
| goto error; |
| } |
| return status; |
| error: |
| qdf_mem_free(addstareq); |
| return status; |
| } |
| |
| static QDF_STATUS |
| tdls_internal_add_peer_rsp(struct tdls_add_peer_request *req, |
| QDF_STATUS status) |
| { |
| struct tdls_soc_priv_obj *soc_obj; |
| struct wlan_objmgr_vdev *vdev; |
| struct tdls_osif_indication ind; |
| QDF_STATUS ret; |
| |
| if (!req || !req->vdev) { |
| tdls_err("req: %pK", req); |
| return QDF_STATUS_E_INVAL; |
| } |
| vdev = req->vdev; |
| ret = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_SB_ID); |
| if (QDF_IS_STATUS_ERROR(ret)) { |
| tdls_err("can't get vdev object"); |
| return ret; |
| } |
| |
| soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); |
| if (soc_obj && soc_obj->tdls_event_cb) { |
| ind.vdev = vdev; |
| ind.status = status; |
| soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, |
| TDLS_EVENT_ADD_PEER, &ind); |
| } |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| static QDF_STATUS |
| tdls_internal_update_peer_rsp(struct tdls_update_peer_request *req, |
| QDF_STATUS status) |
| { |
| struct tdls_soc_priv_obj *soc_obj; |
| struct tdls_osif_indication ind; |
| struct wlan_objmgr_vdev *vdev; |
| QDF_STATUS ret; |
| |
| if (!req || !req->vdev) { |
| tdls_err("req: %pK", req); |
| return QDF_STATUS_E_INVAL; |
| } |
| vdev = req->vdev; |
| ret = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_SB_ID); |
| if (QDF_IS_STATUS_ERROR(ret)) { |
| tdls_err("can't get vdev object"); |
| return ret; |
| } |
| |
| soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); |
| if (soc_obj && soc_obj->tdls_event_cb) { |
| ind.vdev = vdev; |
| ind.status = status; |
| soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, |
| TDLS_EVENT_ADD_PEER, &ind); |
| } |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| static QDF_STATUS tdls_internal_del_peer_rsp(struct tdls_oper_request *req) |
| { |
| struct tdls_soc_priv_obj *soc_obj; |
| struct tdls_osif_indication ind; |
| struct wlan_objmgr_vdev *vdev; |
| QDF_STATUS status; |
| |
| if (!req || !req->vdev) { |
| tdls_err("req: %pK", req); |
| return QDF_STATUS_E_INVAL; |
| } |
| vdev = req->vdev; |
| status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| tdls_err("can't get vdev object"); |
| return status; |
| } |
| |
| soc_obj = wlan_vdev_get_tdls_soc_obj(req->vdev); |
| if (soc_obj && soc_obj->tdls_event_cb) { |
| ind.vdev = req->vdev; |
| soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, |
| TDLS_EVENT_DEL_PEER, &ind); |
| } |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| static QDF_STATUS tdls_activate_add_peer(struct tdls_add_peer_request *req) |
| { |
| QDF_STATUS status; |
| struct tdls_soc_priv_obj *soc_obj; |
| struct tdls_vdev_priv_obj *vdev_obj; |
| struct tdls_peer *peer; |
| uint16_t curr_tdls_peers; |
| const uint8_t *mac; |
| struct tdls_osif_indication ind; |
| |
| if (!req->vdev) { |
| tdls_err("vdev null when add tdls peer"); |
| QDF_ASSERT(0); |
| return QDF_STATUS_E_NULL_VALUE; |
| } |
| |
| mac = req->add_peer_req.peer_addr; |
| soc_obj = wlan_vdev_get_tdls_soc_obj(req->vdev); |
| vdev_obj = wlan_vdev_get_tdls_vdev_obj(req->vdev); |
| |
| if (!soc_obj || !vdev_obj) { |
| tdls_err("soc_obj: %pK, vdev_obj: %pK", soc_obj, vdev_obj); |
| return QDF_STATUS_E_INVAL; |
| } |
| status = tdls_validate_current_mode(soc_obj); |
| if (QDF_IS_STATUS_ERROR(status)) |
| goto addrsp; |
| |
| peer = tdls_get_peer(vdev_obj, mac); |
| if (!peer) { |
| tdls_err("peer: " QDF_MAC_ADDR_STR " not exist. invalid", |
| QDF_MAC_ADDR_ARRAY(mac)); |
| status = QDF_STATUS_E_INVAL; |
| goto addrsp; |
| } |
| |
| /* in add station, we accept existing valid sta_id if there is */ |
| if ((peer->link_status > TDLS_LINK_CONNECTING) || |
| (TDLS_STA_INDEX_CHECK((peer->sta_id)))) { |
| tdls_notice("link_status %d sta_id %d add peer ignored", |
| peer->link_status, peer->sta_id); |
| status = QDF_STATUS_SUCCESS; |
| goto addrsp; |
| } |
| |
| /* when others are on-going, we want to change link_status to idle */ |
| if (tdls_is_progress(vdev_obj, mac, true)) { |
| tdls_notice(QDF_MAC_ADDR_STR " TDLS setuping. Req declined.", |
| QDF_MAC_ADDR_ARRAY(mac)); |
| status = QDF_STATUS_E_PERM; |
| goto setlink; |
| } |
| |
| /* first to check if we reached to maximum supported TDLS peer. */ |
| curr_tdls_peers = tdls_get_connected_peer(soc_obj); |
| if (soc_obj->max_num_tdls_sta <= curr_tdls_peers) { |
| tdls_err(QDF_MAC_ADDR_STR |
| " Request declined. Current %d, Max allowed %d.", |
| QDF_MAC_ADDR_ARRAY(mac), curr_tdls_peers, |
| soc_obj->max_num_tdls_sta); |
| status = QDF_STATUS_E_PERM; |
| goto setlink; |
| } |
| |
| tdls_set_peer_link_status(peer, |
| TDLS_LINK_CONNECTING, TDLS_LINK_SUCCESS); |
| |
| status = tdls_pe_add_peer(req); |
| if (QDF_IS_STATUS_ERROR(status)) |
| goto setlink; |
| |
| return QDF_STATUS_SUCCESS; |
| |
| setlink: |
| tdls_set_link_status(vdev_obj, mac, TDLS_LINK_IDLE, |
| TDLS_LINK_UNSPECIFIED); |
| addrsp: |
| if (soc_obj->tdls_event_cb) { |
| ind.status = status; |
| ind.vdev = req->vdev; |
| soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, |
| TDLS_EVENT_ADD_PEER, &ind); |
| } |
| |
| return QDF_STATUS_E_PERM; |
| } |
| |
| static QDF_STATUS |
| tdls_add_peer_serialize_callback(struct wlan_serialization_command *cmd, |
| enum wlan_serialization_cb_reason reason) |
| { |
| struct tdls_add_peer_request *req; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| |
| if (!cmd || !cmd->umac_cmd) { |
| tdls_err("cmd: %pK, reason: %d", cmd, reason); |
| return QDF_STATUS_E_NULL_VALUE; |
| } |
| |
| req = cmd->umac_cmd; |
| tdls_debug("reason: %d, req %pK", reason, req); |
| |
| switch (reason) { |
| case WLAN_SER_CB_ACTIVATE_CMD: |
| /* command moved to active list |
| */ |
| status = tdls_activate_add_peer(req); |
| break; |
| |
| case WLAN_SER_CB_CANCEL_CMD: |
| /* command removed from pending list. |
| * notify os interface the status |
| */ |
| status = tdls_internal_add_peer_rsp(req, QDF_STATUS_E_FAILURE); |
| break; |
| |
| case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT: |
| /* active command time out. */ |
| status = tdls_internal_add_peer_rsp(req, QDF_STATUS_E_FAILURE); |
| break; |
| |
| case WLAN_SER_CB_RELEASE_MEM_CMD: |
| /* command successfully completed. |
| * release memory & vdev reference count |
| */ |
| wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); |
| qdf_mem_free(req); |
| break; |
| |
| default: |
| /* Do nothing but logging */ |
| QDF_ASSERT(0); |
| status = QDF_STATUS_E_INVAL; |
| break; |
| } |
| |
| return status; |
| } |
| |
| void tdls_reset_nss(struct tdls_soc_priv_obj *tdls_soc, |
| uint8_t action_code) |
| { |
| if (!tdls_soc) |
| return; |
| |
| if (TDLS_TEARDOWN != action_code || |
| !tdls_soc->tdls_nss_switch_in_progress) |
| return; |
| |
| if (tdls_soc->tdls_teardown_peers_cnt != 0) |
| tdls_soc->tdls_teardown_peers_cnt--; |
| if (tdls_soc->tdls_teardown_peers_cnt == 0) { |
| if (tdls_soc->tdls_nss_transition_mode == |
| TDLS_NSS_TRANSITION_S_1x1_to_2x2) { |
| /* TDLS NSS switch is fully completed, so |
| * reset the flags. |
| */ |
| tdls_notice("TDLS NSS switch is fully completed"); |
| tdls_soc->tdls_nss_switch_in_progress = false; |
| tdls_soc->tdls_nss_teardown_complete = false; |
| } else { |
| /* TDLS NSS switch is not yet completed, but |
| * tdls teardown is completed for all the |
| * peers. |
| */ |
| tdls_notice("teardown done & NSS switch in progress"); |
| tdls_soc->tdls_nss_teardown_complete = true; |
| } |
| tdls_soc->tdls_nss_transition_mode = |
| TDLS_NSS_TRANSITION_S_UNKNOWN; |
| } |
| |
| } |
| |
| /** |
| * tdls_set_cap() - set TDLS capability type |
| * @tdls_vdev: tdls vdev object |
| * @mac: peer mac address |
| * @cap: TDLS capability type |
| * |
| * Return: 0 if successful or negative errno otherwise |
| */ |
| int tdls_set_cap(struct tdls_vdev_priv_obj *tdls_vdev, const uint8_t *mac, |
| enum tdls_peer_capab cap) |
| { |
| struct tdls_peer *curr_peer; |
| |
| curr_peer = tdls_get_peer(tdls_vdev, mac); |
| if (curr_peer == NULL) { |
| tdls_err("curr_peer is NULL"); |
| return -EINVAL; |
| } |
| |
| curr_peer->tdls_support = cap; |
| return 0; |
| } |
| |
| static int tdls_validate_setup_frames(struct tdls_soc_priv_obj *tdls_soc, |
| struct tdls_validate_action_req *tdls_validate) |
| { |
| /* supplicant still sends tdls_mgmt(SETUP_REQ) |
| * even after we return error code at |
| * 'add_station()'. Hence we have this check |
| * again in addition to add_station(). Anyway, |
| * there is no harm to double-check. |
| */ |
| if (TDLS_SETUP_REQUEST == tdls_validate->action_code) { |
| tdls_err(QDF_MAC_ADDR_STR " TDLS Max peer already connected. action (%d) declined. Num of peers (%d), Max allowed (%d).", |
| QDF_MAC_ADDR_ARRAY(tdls_validate->peer_mac), |
| tdls_validate->action_code, |
| tdls_soc->connected_peer_count, |
| tdls_soc->max_num_tdls_sta); |
| return -EINVAL; |
| } |
| /* maximum reached. tweak to send |
| * error code to peer and return error |
| * code to supplicant |
| */ |
| tdls_validate->status_code = QDF_STATUS_E_RESOURCES; |
| tdls_err(QDF_MAC_ADDR_STR " TDLS Max peer already connected, send response status (%d). Num of peers (%d), Max allowed (%d).", |
| QDF_MAC_ADDR_ARRAY(tdls_validate->peer_mac), |
| tdls_validate->action_code, |
| tdls_soc->connected_peer_count, |
| tdls_soc->max_num_tdls_sta); |
| |
| tdls_validate->max_sta_failed = -EPERM; |
| return 0; |
| } |
| |
| int tdls_validate_mgmt_request(struct tdls_action_frame_request *tdls_mgmt_req) |
| { |
| struct tdls_vdev_priv_obj *tdls_vdev; |
| struct tdls_soc_priv_obj *tdls_soc; |
| struct tdls_peer *curr_peer; |
| struct tdls_peer *temp_peer; |
| QDF_STATUS status; |
| uint8_t vdev_id; |
| |
| struct tdls_validate_action_req *tdls_validate = |
| tdls_mgmt_req->chk_frame; |
| |
| if (!tdls_validate || !tdls_validate->vdev) |
| return -EINVAL; |
| |
| if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(tdls_validate->vdev, |
| &tdls_vdev, |
| &tdls_soc)) |
| return -ENOTSUPP; |
| |
| /* |
| * STA or P2P client should be connected and authenticated before |
| * sending any TDLS frames |
| */ |
| if (!tdls_is_vdev_connected(tdls_validate->vdev) || |
| !tdls_is_vdev_authenticated(tdls_validate->vdev)) { |
| tdls_err("STA is not connected or not authenticated."); |
| return -EAGAIN; |
| } |
| |
| /* other than teardown frame, mgmt frames are not sent if disabled */ |
| if (TDLS_TEARDOWN != tdls_validate->action_code) { |
| if (!tdls_check_is_tdls_allowed(tdls_validate->vdev)) { |
| tdls_err("TDLS not allowed, reject MGMT, action = %d", |
| tdls_validate->action_code); |
| return -EPERM; |
| } |
| /* if tdls_mode is disabled, then decline the peer's request */ |
| if (TDLS_SUPPORT_DISABLED == tdls_soc->tdls_current_mode || |
| TDLS_SUPPORT_SUSPENDED == tdls_soc->tdls_current_mode) { |
| tdls_notice(QDF_MAC_ADDR_STR |
| " TDLS mode is disabled. action %d declined.", |
| QDF_MAC_ADDR_ARRAY(tdls_validate->peer_mac), |
| tdls_validate->action_code); |
| return -ENOTSUPP; |
| } |
| if (tdls_soc->tdls_nss_switch_in_progress) { |
| tdls_err("nss switch in progress, action %d declined " |
| QDF_MAC_ADDR_STR, |
| tdls_validate->action_code, |
| QDF_MAC_ADDR_ARRAY(tdls_validate->peer_mac)); |
| return -EAGAIN; |
| } |
| } |
| |
| if (TDLS_IS_SETUP_ACTION(tdls_validate->action_code)) { |
| if (NULL != tdls_is_progress(tdls_vdev, |
| tdls_validate->peer_mac, true)) { |
| tdls_err("setup is ongoing. action %d declined for " |
| QDF_MAC_ADDR_STR, |
| tdls_validate->action_code, |
| QDF_MAC_ADDR_ARRAY(tdls_validate->peer_mac)); |
| return -EPERM; |
| } |
| } |
| |
| /* call hdd_wmm_is_acm_allowed() */ |
| vdev_id = wlan_vdev_get_id(tdls_validate->vdev); |
| if (!tdls_soc->tdls_wmm_cb(vdev_id)) { |
| tdls_debug("admission ctrl set to VI, send the frame with least AC (BK) for action %d", |
| tdls_validate->action_code); |
| tdls_mgmt_req->use_default_ac = false; |
| } else { |
| tdls_mgmt_req->use_default_ac = true; |
| } |
| |
| if (TDLS_SETUP_REQUEST == tdls_validate->action_code || |
| TDLS_SETUP_RESPONSE == tdls_validate->action_code) { |
| if (tdls_soc->max_num_tdls_sta <= |
| tdls_soc->connected_peer_count) { |
| status = tdls_validate_setup_frames(tdls_soc, |
| tdls_validate); |
| if (QDF_STATUS_SUCCESS != status) |
| return status; |
| /* fall through to send setup resp |
| * with failure status code |
| */ |
| } else { |
| curr_peer = |
| tdls_find_peer(tdls_vdev, |
| tdls_validate->peer_mac); |
| if (curr_peer) { |
| if (TDLS_IS_LINK_CONNECTED(curr_peer)) { |
| tdls_err(QDF_MAC_ADDR_STR " already connected action %d declined.", |
| QDF_MAC_ADDR_ARRAY( |
| tdls_validate->peer_mac), |
| tdls_validate->action_code); |
| |
| return -EPERM; |
| } |
| } |
| } |
| } |
| |
| tdls_notice("tdls_mgmt" QDF_MAC_ADDR_STR " action %d, dialog_token %d status %d, len = %zu", |
| QDF_MAC_ADDR_ARRAY(tdls_validate->peer_mac), |
| tdls_validate->action_code, tdls_validate->dialog_token, |
| tdls_validate->status_code, tdls_validate->len); |
| |
| /*Except teardown responder will not be used so just make 0 */ |
| tdls_validate->responder = 0; |
| if (TDLS_TEARDOWN == tdls_validate->action_code) { |
| temp_peer = tdls_find_peer(tdls_vdev, tdls_validate->peer_mac); |
| if (!temp_peer) { |
| tdls_err(QDF_MAC_ADDR_STR " peer doesn't exist", |
| QDF_MAC_ADDR_ARRAY( |
| tdls_validate->peer_mac)); |
| return -EPERM; |
| } |
| |
| if (TDLS_IS_LINK_CONNECTED(temp_peer)) |
| tdls_validate->responder = temp_peer->is_responder; |
| else { |
| tdls_err(QDF_MAC_ADDR_STR " peer doesn't exist or not connected %d dialog_token %d status %d, tdls_validate->len = %zu", |
| QDF_MAC_ADDR_ARRAY(tdls_validate->peer_mac), |
| temp_peer->link_status, |
| tdls_validate->dialog_token, |
| tdls_validate->status_code, |
| tdls_validate->len); |
| return -EPERM; |
| } |
| } |
| |
| /* For explicit trigger of DIS_REQ come out of BMPS for |
| * successfully receiving DIS_RSP from peer. |
| */ |
| if ((TDLS_SETUP_RESPONSE == tdls_validate->action_code) || |
| (TDLS_SETUP_CONFIRM == tdls_validate->action_code) || |
| (TDLS_DISCOVERY_RESPONSE == tdls_validate->action_code) || |
| (TDLS_DISCOVERY_REQUEST == tdls_validate->action_code)) { |
| /* Fw will take care if PS offload is enabled. */ |
| if (TDLS_DISCOVERY_REQUEST != tdls_validate->action_code) |
| tdls_set_cap(tdls_vdev, tdls_validate->peer_mac, |
| TDLS_CAP_SUPPORTED); |
| } |
| return 0; |
| } |
| |
| QDF_STATUS tdls_process_add_peer(struct tdls_add_peer_request *req) |
| { |
| struct wlan_serialization_command cmd = {0,}; |
| enum wlan_serialization_status ser_cmd_status; |
| struct wlan_objmgr_vdev *vdev; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| |
| if (!req || !req->vdev) { |
| tdls_err("req: %pK", req); |
| status = QDF_STATUS_E_INVAL; |
| goto error; |
| } |
| vdev = req->vdev; |
| cmd.cmd_type = WLAN_SER_CMD_TDLS_ADD_PEER; |
| cmd.cmd_id = 0; |
| cmd.cmd_cb = (wlan_serialization_cmd_callback) |
| tdls_add_peer_serialize_callback; |
| cmd.umac_cmd = req; |
| cmd.source = WLAN_UMAC_COMP_TDLS; |
| cmd.is_high_priority = false; |
| cmd.cmd_timeout_duration = TDLS_DEFAULT_SERIALIZE_CMD_TIMEOUT; |
| cmd.vdev = vdev; |
| cmd.is_blocking = true; |
| |
| ser_cmd_status = wlan_serialization_request(&cmd); |
| tdls_debug("req: 0x%pK wlan_serialization_request status:%d", req, |
| ser_cmd_status); |
| |
| 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; |
| case WLAN_SER_CMD_DENIED_LIST_FULL: |
| case WLAN_SER_CMD_DENIED_RULES_FAILED: |
| case WLAN_SER_CMD_DENIED_UNSPECIFIED: |
| /* notify os interface about internal error*/ |
| status = tdls_internal_add_peer_rsp(req, QDF_STATUS_E_FAILURE); |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); |
| /* cmd can't be serviced. |
| * release tdls_add_peer_request memory |
| */ |
| qdf_mem_free(req); |
| break; |
| default: |
| QDF_ASSERT(0); |
| status = QDF_STATUS_E_INVAL; |
| goto error; |
| } |
| |
| return status; |
| error: |
| status = tdls_internal_add_peer_rsp(req, QDF_STATUS_E_FAILURE); |
| qdf_mem_free(req); |
| return status; |
| } |
| |
| static QDF_STATUS |
| tdls_activate_update_peer(struct tdls_update_peer_request *req) |
| { |
| QDF_STATUS status; |
| struct tdls_soc_priv_obj *soc_obj; |
| struct tdls_vdev_priv_obj *vdev_obj; |
| struct wlan_objmgr_vdev *vdev; |
| struct tdls_peer *curr_peer; |
| uint16_t curr_tdls_peers; |
| const uint8_t *mac; |
| struct tdls_update_peer_params *update_peer; |
| struct tdls_osif_indication ind; |
| |
| if (!req->vdev) { |
| tdls_err("vdev object NULL when add TDLS peer"); |
| QDF_ASSERT(0); |
| return QDF_STATUS_E_NULL_VALUE; |
| } |
| |
| mac = req->update_peer_req.peer_addr; |
| vdev = req->vdev; |
| soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); |
| vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); |
| if (!soc_obj || !vdev_obj) { |
| tdls_err("soc_obj: %pK, vdev_obj: %pK", soc_obj, vdev_obj); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| status = tdls_validate_current_mode(soc_obj); |
| if (QDF_IS_STATUS_ERROR(status)) |
| goto updatersp; |
| |
| curr_peer = tdls_get_peer(vdev_obj, mac); |
| if (!curr_peer) { |
| tdls_err(QDF_MAC_ADDR_STR " not exist. return invalid", |
| QDF_MAC_ADDR_ARRAY(mac)); |
| status = QDF_STATUS_E_INVAL; |
| goto updatersp; |
| } |
| |
| /* in change station, we accept only when sta_id is valid */ |
| if (curr_peer->link_status > TDLS_LINK_CONNECTING || |
| !(TDLS_STA_INDEX_CHECK(curr_peer->sta_id))) { |
| tdls_err(QDF_MAC_ADDR_STR " link %d. sta %d. update peer %s", |
| QDF_MAC_ADDR_ARRAY(mac), curr_peer->link_status, |
| curr_peer->sta_id, |
| (TDLS_STA_INDEX_CHECK(curr_peer->sta_id)) ? "ignored" |
| : "declined"); |
| status = (TDLS_STA_INDEX_CHECK(curr_peer->sta_id)) ? |
| QDF_STATUS_SUCCESS : QDF_STATUS_E_PERM; |
| goto updatersp; |
| } |
| |
| /* when others are on-going, we want to change link_status to idle */ |
| if (tdls_is_progress(vdev_obj, mac, true)) { |
| tdls_notice(QDF_MAC_ADDR_STR " TDLS setuping. Req declined.", |
| QDF_MAC_ADDR_ARRAY(mac)); |
| status = QDF_STATUS_E_PERM; |
| goto setlink; |
| } |
| |
| curr_tdls_peers = tdls_get_connected_peer(soc_obj); |
| if (soc_obj->max_num_tdls_sta <= curr_tdls_peers) { |
| tdls_err(QDF_MAC_ADDR_STR |
| " Request declined. Current: %d, Max allowed: %d.", |
| QDF_MAC_ADDR_ARRAY(mac), curr_tdls_peers, |
| soc_obj->max_num_tdls_sta); |
| status = QDF_STATUS_E_PERM; |
| goto setlink; |
| } |
| update_peer = &req->update_peer_req; |
| |
| if (update_peer->htcap_present) |
| curr_peer->spatial_streams = update_peer->ht_cap.mcsset[1]; |
| |
| tdls_set_peer_caps(vdev_obj, mac, &req->update_peer_req); |
| status = tdls_pe_update_peer(req); |
| if (QDF_IS_STATUS_ERROR(status)) |
| goto setlink; |
| |
| return QDF_STATUS_SUCCESS; |
| |
| setlink: |
| tdls_set_link_status(vdev_obj, mac, TDLS_LINK_IDLE, |
| TDLS_LINK_UNSPECIFIED); |
| updatersp: |
| if (soc_obj->tdls_event_cb) { |
| ind.status = status; |
| ind.vdev = vdev; |
| soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, |
| TDLS_EVENT_ADD_PEER, &ind); |
| } |
| |
| return QDF_STATUS_E_PERM; |
| } |
| |
| static QDF_STATUS |
| tdls_update_peer_serialize_callback(struct wlan_serialization_command *cmd, |
| enum wlan_serialization_cb_reason reason) |
| { |
| struct tdls_update_peer_request *req; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| |
| if (!cmd || !cmd->umac_cmd) { |
| tdls_err("cmd: %pK, reason: %d", cmd, reason); |
| return QDF_STATUS_E_NULL_VALUE; |
| } |
| |
| req = cmd->umac_cmd; |
| tdls_debug("reason: %d, req %pK", reason, req); |
| |
| switch (reason) { |
| case WLAN_SER_CB_ACTIVATE_CMD: |
| /* command moved to active list |
| */ |
| status = tdls_activate_update_peer(req); |
| break; |
| |
| case WLAN_SER_CB_CANCEL_CMD: |
| /* command removed from pending list. |
| * notify os interface the status |
| */ |
| status = tdls_internal_update_peer_rsp(req, |
| QDF_STATUS_E_FAILURE); |
| break; |
| |
| case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT: |
| /* active command time out. */ |
| status = tdls_internal_update_peer_rsp(req, |
| QDF_STATUS_E_FAILURE); |
| break; |
| |
| case WLAN_SER_CB_RELEASE_MEM_CMD: |
| /* command successfully completed. |
| * release memory & release reference count |
| */ |
| wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); |
| qdf_mem_free(req); |
| break; |
| |
| default: |
| /* Do nothing but logging */ |
| QDF_ASSERT(0); |
| status = QDF_STATUS_E_INVAL; |
| break; |
| } |
| |
| return status; |
| } |
| |
| QDF_STATUS tdls_process_update_peer(struct tdls_update_peer_request *req) |
| { |
| struct wlan_serialization_command cmd = {0,}; |
| enum wlan_serialization_status ser_cmd_status; |
| struct wlan_objmgr_vdev *vdev; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| |
| if (!req || !req->vdev) { |
| tdls_err("req: %pK", req); |
| status = QDF_STATUS_E_FAILURE; |
| goto error; |
| } |
| |
| vdev = req->vdev; |
| cmd.cmd_type = WLAN_SER_CMD_TDLS_ADD_PEER; |
| cmd.cmd_id = 0; |
| cmd.cmd_cb = (wlan_serialization_cmd_callback) |
| tdls_update_peer_serialize_callback; |
| cmd.umac_cmd = req; |
| cmd.source = WLAN_UMAC_COMP_TDLS; |
| cmd.is_high_priority = false; |
| cmd.cmd_timeout_duration = TDLS_DEFAULT_SERIALIZE_CMD_TIMEOUT; |
| cmd.vdev = req->vdev; |
| cmd.is_blocking = true; |
| |
| ser_cmd_status = wlan_serialization_request(&cmd); |
| tdls_debug("req: 0x%pK wlan_serialization_request status:%d", req, |
| ser_cmd_status); |
| |
| 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; |
| case WLAN_SER_CMD_DENIED_LIST_FULL: |
| case WLAN_SER_CMD_DENIED_RULES_FAILED: |
| case WLAN_SER_CMD_DENIED_UNSPECIFIED: |
| /* notify os interface about internal error*/ |
| status = tdls_internal_update_peer_rsp(req, |
| QDF_STATUS_E_FAILURE); |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); |
| /* cmd can't be serviced. |
| * release tdls_add_peer_request memory |
| */ |
| qdf_mem_free(req); |
| break; |
| default: |
| QDF_ASSERT(0); |
| status = QDF_STATUS_E_INVAL; |
| break; |
| } |
| |
| return status; |
| error: |
| status = tdls_internal_update_peer_rsp(req, QDF_STATUS_E_FAILURE); |
| qdf_mem_free(req); |
| return status; |
| } |
| |
| static QDF_STATUS tdls_activate_del_peer(struct tdls_oper_request *req) |
| { |
| struct tdls_del_peer_request request = {0,}; |
| |
| request.vdev = req->vdev; |
| request.del_peer_req.peer_addr = req->peer_addr; |
| |
| return tdls_pe_del_peer(&request); |
| } |
| |
| static QDF_STATUS |
| tdls_del_peer_serialize_callback(struct wlan_serialization_command *cmd, |
| enum wlan_serialization_cb_reason reason) |
| { |
| struct tdls_oper_request *req; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| |
| if (!cmd || !cmd->umac_cmd) { |
| tdls_err("cmd: %pK, reason: %d", cmd, reason); |
| return QDF_STATUS_E_NULL_VALUE; |
| } |
| |
| req = cmd->umac_cmd; |
| tdls_debug("reason: %d, req %pK", reason, req); |
| |
| switch (reason) { |
| case WLAN_SER_CB_ACTIVATE_CMD: |
| /* command moved to active list |
| */ |
| status = tdls_activate_del_peer(req); |
| break; |
| |
| case WLAN_SER_CB_CANCEL_CMD: |
| /* command removed from pending list. |
| * notify os interface the status |
| */ |
| status = tdls_internal_del_peer_rsp(req); |
| break; |
| |
| case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT: |
| /* active command time out. */ |
| status = tdls_internal_del_peer_rsp(req); |
| break; |
| |
| case WLAN_SER_CB_RELEASE_MEM_CMD: |
| /* command successfully completed. |
| * release memory & vdev reference count |
| */ |
| wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); |
| qdf_mem_free(req); |
| break; |
| |
| default: |
| /* Do nothing but logging */ |
| QDF_ASSERT(0); |
| status = QDF_STATUS_E_INVAL; |
| break; |
| } |
| |
| return status; |
| } |
| |
| QDF_STATUS tdls_process_del_peer(struct tdls_oper_request *req) |
| { |
| struct wlan_serialization_command cmd = {0,}; |
| enum wlan_serialization_status ser_cmd_status; |
| struct wlan_objmgr_vdev *vdev; |
| struct tdls_vdev_priv_obj *vdev_obj; |
| struct tdls_soc_priv_obj *soc_obj; |
| uint8_t *mac; |
| struct tdls_peer *peer; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| |
| if (!req || !req->vdev) { |
| tdls_err("req: %pK", req); |
| status = QDF_STATUS_E_INVAL; |
| goto free_req; |
| } |
| |
| vdev = req->vdev; |
| |
| /* vdev reference cnt is acquired in ucfg_tdls_oper */ |
| vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); |
| soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); |
| |
| if (!vdev_obj || !soc_obj) { |
| tdls_err("tdls vdev_obj: %pK soc_obj: %pK", vdev_obj, soc_obj); |
| status = QDF_STATUS_E_NULL_VALUE; |
| goto error; |
| } |
| |
| mac = req->peer_addr; |
| peer = tdls_find_peer(vdev_obj, mac); |
| if (!peer) { |
| tdls_err(QDF_MAC_ADDR_STR |
| " not found, ignore NL80211_TDLS_ENABLE_LINK", |
| QDF_MAC_ADDR_ARRAY(mac)); |
| status = QDF_STATUS_E_INVAL; |
| goto error; |
| } |
| |
| if (soc_obj->tdls_dp_vdev_update) |
| soc_obj->tdls_dp_vdev_update(&soc_obj->soc, |
| peer->sta_id, |
| soc_obj->tdls_update_dp_vdev_flags, |
| false); |
| |
| cmd.cmd_type = WLAN_SER_CMD_TDLS_DEL_PEER; |
| cmd.cmd_id = 0; |
| cmd.cmd_cb = (wlan_serialization_cmd_callback) |
| tdls_del_peer_serialize_callback; |
| cmd.umac_cmd = req; |
| cmd.source = WLAN_UMAC_COMP_TDLS; |
| cmd.is_high_priority = false; |
| cmd.cmd_timeout_duration = TDLS_DEFAULT_SERIALIZE_CMD_TIMEOUT; |
| cmd.vdev = vdev; |
| cmd.is_blocking = true; |
| |
| ser_cmd_status = wlan_serialization_request(&cmd); |
| tdls_debug("req: 0x%pK wlan_serialization_request status:%d", req, |
| ser_cmd_status); |
| |
| 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; |
| case WLAN_SER_CMD_DENIED_LIST_FULL: |
| case WLAN_SER_CMD_DENIED_RULES_FAILED: |
| case WLAN_SER_CMD_DENIED_UNSPECIFIED: |
| /* notify os interface about internal error*/ |
| status = tdls_internal_del_peer_rsp(req); |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); |
| /* cmd can't be serviced. |
| * release tdls_add_peer_request memory |
| */ |
| qdf_mem_free(req); |
| break; |
| default: |
| QDF_ASSERT(0); |
| status = QDF_STATUS_E_INVAL; |
| break; |
| } |
| |
| return status; |
| error: |
| status = tdls_internal_del_peer_rsp(req); |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); |
| free_req: |
| qdf_mem_free(req); |
| return status; |
| } |
| |
| /** |
| * tdls_process_add_peer_rsp() - handle response for update TDLS peer |
| * @rsp: TDLS add peer response |
| * |
| * Return: QDF_STATUS_SUCCESS for success; other values if failed |
| */ |
| static QDF_STATUS tdls_update_peer_rsp(struct tdls_add_sta_rsp *rsp) |
| { |
| struct wlan_objmgr_vdev *vdev; |
| struct wlan_objmgr_psoc *psoc; |
| struct tdls_soc_priv_obj *soc_obj; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| struct tdls_osif_indication ind; |
| |
| psoc = rsp->psoc; |
| if (!psoc) { |
| tdls_err("psoc is NULL"); |
| QDF_ASSERT(0); |
| return QDF_STATUS_E_FAILURE; |
| } |
| vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, rsp->session_id, |
| WLAN_TDLS_SB_ID); |
| if (!vdev) { |
| tdls_err("invalid vdev: %d", rsp->session_id); |
| status = QDF_STATUS_E_INVAL; |
| goto error; |
| } |
| |
| tdls_release_serialization_command(vdev, WLAN_SER_CMD_TDLS_ADD_PEER); |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); |
| error: |
| soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); |
| if (soc_obj && soc_obj->tdls_event_cb) { |
| ind.status = rsp->status_code; |
| ind.vdev = vdev; |
| soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, |
| TDLS_EVENT_ADD_PEER, &ind); |
| } |
| qdf_mem_free(rsp); |
| |
| return status; |
| } |
| |
| /** |
| * tdls_process_send_mgmt_rsp() - handle response for send mgmt |
| * @rsp: TDLS send mgmt response |
| * |
| * Return: QDF_STATUS_SUCCESS for success; other values if failed |
| */ |
| QDF_STATUS tdls_process_send_mgmt_rsp(struct tdls_send_mgmt_rsp *rsp) |
| { |
| struct wlan_objmgr_vdev *vdev; |
| struct wlan_objmgr_psoc *psoc; |
| struct tdls_vdev_priv_obj *tdls_vdev; |
| struct tdls_soc_priv_obj *tdls_soc = NULL; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| struct tdls_osif_indication ind; |
| |
| psoc = rsp->psoc; |
| vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, rsp->session_id, |
| WLAN_TDLS_SB_ID); |
| if (!vdev) { |
| tdls_err("invalid vdev"); |
| status = QDF_STATUS_E_INVAL; |
| qdf_mem_free(rsp); |
| return status; |
| } |
| tdls_soc = wlan_psoc_get_tdls_soc_obj(psoc); |
| tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev); |
| if (!tdls_soc || !tdls_vdev) { |
| tdls_err("soc object:%pK, vdev object:%pK", tdls_soc, tdls_vdev); |
| status = QDF_STATUS_E_FAILURE; |
| } |
| |
| tdls_release_serialization_command(vdev, WLAN_SER_CMD_TDLS_SEND_MGMT); |
| |
| if (legacy_result_success == rsp->status_code) |
| goto free_rsp; |
| tdls_err("send mgmt failed. status code(=%d)", rsp->status_code); |
| status = QDF_STATUS_E_FAILURE; |
| |
| if (tdls_soc && tdls_soc->tdls_event_cb) { |
| ind.vdev = vdev; |
| ind.status = status; |
| tdls_soc->tdls_event_cb(tdls_soc->tdls_evt_cb_data, |
| TDLS_EVENT_MGMT_TX_ACK_CNF, &ind); |
| } |
| |
| free_rsp: |
| qdf_mem_free(rsp); |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); |
| return status; |
| } |
| |
| /** |
| * tdls_send_mgmt_tx_completion() - process tx completion |
| * @tx_complete: TDLS mgmt completion info |
| * |
| * Return: QDF_STATUS_SUCCESS for success; other values if failed |
| */ |
| QDF_STATUS tdls_send_mgmt_tx_completion( |
| struct tdls_mgmt_tx_completion_ind *tx_complete) |
| { |
| struct wlan_objmgr_vdev *vdev; |
| struct wlan_objmgr_psoc *psoc; |
| struct tdls_vdev_priv_obj *tdls_vdev; |
| struct tdls_soc_priv_obj *tdls_soc = NULL; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| struct tdls_osif_indication ind; |
| |
| psoc = tx_complete->psoc; |
| vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, |
| tx_complete->session_id, |
| WLAN_TDLS_SB_ID); |
| |
| if (!vdev) { |
| tdls_err("invalid vdev"); |
| status = QDF_STATUS_E_INVAL; |
| goto free_tx_complete; |
| } |
| |
| tdls_soc = wlan_psoc_get_tdls_soc_obj(psoc); |
| tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev); |
| |
| if (!tdls_soc || !tdls_vdev) { |
| tdls_err("soc object:%pK, vdev object:%pK", tdls_soc, tdls_vdev); |
| status = QDF_STATUS_E_FAILURE; |
| } |
| |
| if (tdls_soc && tdls_soc->tdls_event_cb) { |
| ind.vdev = vdev; |
| ind.status = tx_complete->tx_complete_status; |
| tdls_soc->tdls_event_cb(tdls_soc->tdls_evt_cb_data, |
| TDLS_EVENT_MGMT_TX_ACK_CNF, &ind); |
| } |
| |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); |
| free_tx_complete: |
| qdf_mem_free(tx_complete); |
| return status; |
| } |
| |
| /** |
| * tdls_add_peer_rsp() - handle response for add TDLS peer |
| * @rsp: TDLS add peer response |
| * |
| * Return: QDF_STATUS_SUCCESS for success; other values if failed |
| */ |
| static QDF_STATUS tdls_add_peer_rsp(struct tdls_add_sta_rsp *rsp) |
| { |
| uint8_t sta_idx; |
| struct wlan_objmgr_vdev *vdev; |
| struct wlan_objmgr_psoc *psoc; |
| struct tdls_vdev_priv_obj *vdev_obj; |
| struct tdls_soc_priv_obj *soc_obj = NULL; |
| struct tdls_conn_info *conn_rec; |
| QDF_STATUS status = QDF_STATUS_SUCCESS; |
| struct tdls_osif_indication ind; |
| |
| psoc = rsp->psoc; |
| vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, rsp->session_id, |
| WLAN_TDLS_SB_ID); |
| if (!vdev) { |
| tdls_err("invalid vdev: %d", rsp->session_id); |
| status = QDF_STATUS_E_INVAL; |
| goto error; |
| } |
| soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); |
| vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); |
| if (!soc_obj || !vdev_obj) { |
| tdls_err("soc object:%pK, vdev object:%pK", soc_obj, vdev_obj); |
| status = QDF_STATUS_E_FAILURE; |
| goto cmddone; |
| } |
| if (rsp->status_code) { |
| tdls_err("add sta failed. status code(=%d)", rsp->status_code); |
| status = QDF_STATUS_E_FAILURE; |
| } else { |
| conn_rec = soc_obj->tdls_conn_info; |
| for (sta_idx = 0; sta_idx < soc_obj->max_num_tdls_sta; |
| sta_idx++) { |
| if (INVALID_TDLS_PEER_ID == conn_rec[sta_idx].sta_id) { |
| conn_rec[sta_idx].session_id = rsp->session_id; |
| conn_rec[sta_idx].sta_id = rsp->sta_id; |
| qdf_copy_macaddr(&conn_rec[sta_idx].peer_mac, |
| &rsp->peermac); |
| tdls_warn("TDLS: STA IDX at %d is %d of mac " |
| QDF_MAC_ADDR_STR, sta_idx, |
| rsp->sta_id, QDF_MAC_ADDR_ARRAY |
| (rsp->peermac.bytes)); |
| break; |
| } |
| } |
| |
| if (sta_idx < soc_obj->max_num_tdls_sta) { |
| status = tdls_set_sta_id(vdev_obj, rsp->peermac.bytes, |
| rsp->sta_id); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| tdls_err("set staid failed"); |
| status = QDF_STATUS_E_FAILURE; |
| } |
| } else { |
| status = QDF_STATUS_E_FAILURE; |
| } |
| } |
| |
| cmddone: |
| tdls_release_serialization_command(vdev, WLAN_SER_CMD_TDLS_ADD_PEER); |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); |
| error: |
| if (soc_obj && soc_obj->tdls_event_cb) { |
| ind.vdev = vdev; |
| ind.status = rsp->status_code; |
| soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, |
| TDLS_EVENT_ADD_PEER, &ind); |
| } |
| qdf_mem_free(rsp); |
| |
| return status; |
| } |
| |
| QDF_STATUS tdls_process_add_peer_rsp(struct tdls_add_sta_rsp *rsp) |
| { |
| tdls_debug("peer oper %d", rsp->tdls_oper); |
| |
| if (rsp->tdls_oper == TDLS_OPER_ADD) |
| return tdls_add_peer_rsp(rsp); |
| else if (rsp->tdls_oper == TDLS_OPER_UPDATE) |
| return tdls_update_peer_rsp(rsp); |
| |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| QDF_STATUS tdls_process_del_peer_rsp(struct tdls_del_sta_rsp *rsp) |
| { |
| uint8_t sta_idx, id; |
| QDF_STATUS status = QDF_STATUS_E_FAILURE; |
| struct wlan_objmgr_vdev *vdev; |
| struct wlan_objmgr_psoc *psoc; |
| struct tdls_vdev_priv_obj *vdev_obj; |
| struct tdls_soc_priv_obj *soc_obj = NULL; |
| struct tdls_conn_info *conn_rec; |
| struct tdls_peer *curr_peer = NULL; |
| const uint8_t *macaddr; |
| struct tdls_osif_indication ind; |
| |
| tdls_debug("del peer rsp: vdev %d peer " QDF_MAC_ADDR_STR, |
| rsp->session_id, QDF_MAC_ADDR_ARRAY(rsp->peermac.bytes)); |
| psoc = rsp->psoc; |
| vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, rsp->session_id, |
| WLAN_TDLS_SB_ID); |
| if (!vdev) { |
| tdls_err("invalid vdev: %d", rsp->session_id); |
| status = QDF_STATUS_E_INVAL; |
| goto error; |
| } |
| soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); |
| vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); |
| if (!soc_obj || !vdev_obj) { |
| tdls_err("soc object:%pK, vdev object:%pK", soc_obj, vdev_obj); |
| status = QDF_STATUS_E_FAILURE; |
| goto cmddone; |
| } |
| |
| conn_rec = soc_obj->tdls_conn_info; |
| for (sta_idx = 0; sta_idx < soc_obj->max_num_tdls_sta; sta_idx++) { |
| if (conn_rec[sta_idx].session_id != rsp->session_id || |
| conn_rec[sta_idx].sta_id != rsp->sta_id) |
| continue; |
| |
| macaddr = rsp->peermac.bytes; |
| tdls_warn("TDLS: del STA IDX = %x", rsp->sta_id); |
| curr_peer = tdls_find_peer(vdev_obj, macaddr); |
| if (curr_peer) { |
| tdls_debug(QDF_MAC_ADDR_STR " status is %d", |
| QDF_MAC_ADDR_ARRAY(macaddr), |
| curr_peer->link_status); |
| |
| id = wlan_vdev_get_id(vdev); |
| |
| if (TDLS_IS_LINK_CONNECTED(curr_peer)) { |
| soc_obj->tdls_dereg_peer( |
| soc_obj->tdls_peer_context, |
| id, curr_peer->sta_id); |
| tdls_decrement_peer_count(soc_obj); |
| } else if (TDLS_LINK_CONNECTING == |
| curr_peer->link_status) { |
| soc_obj->tdls_dereg_peer( |
| soc_obj->tdls_peer_context, |
| id, curr_peer->sta_id); |
| } |
| } |
| tdls_reset_peer(vdev_obj, macaddr); |
| conn_rec[sta_idx].sta_id = INVALID_TDLS_PEER_ID; |
| conn_rec[sta_idx].session_id = 0xff; |
| qdf_mem_zero(&conn_rec[sta_idx].peer_mac, |
| QDF_MAC_ADDR_SIZE); |
| |
| status = QDF_STATUS_SUCCESS; |
| break; |
| } |
| macaddr = rsp->peermac.bytes; |
| if (!curr_peer) { |
| curr_peer = tdls_find_peer(vdev_obj, macaddr); |
| |
| if (curr_peer) |
| tdls_set_peer_link_status(curr_peer, TDLS_LINK_IDLE, |
| (curr_peer->link_status == |
| TDLS_LINK_TEARING) ? |
| TDLS_LINK_UNSPECIFIED : |
| TDLS_LINK_DROPPED_BY_REMOTE); |
| } |
| |
| cmddone: |
| tdls_release_serialization_command(vdev, WLAN_SER_CMD_TDLS_DEL_PEER); |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); |
| error: |
| |
| if (soc_obj && soc_obj->tdls_event_cb) { |
| ind.vdev = vdev; |
| soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, |
| TDLS_EVENT_DEL_PEER, &ind); |
| } |
| qdf_mem_free(rsp); |
| |
| return status; |
| } |
| |
| static QDF_STATUS |
| tdls_wma_update_peer_state(struct tdls_soc_priv_obj *soc_obj, |
| struct tdls_peer_update_state *peer_state) |
| { |
| struct scheduler_msg msg = {0,}; |
| QDF_STATUS status; |
| |
| tdls_debug("update TDLS peer " QDF_MAC_ADDR_STR " vdev %d, state %d", |
| QDF_MAC_ADDR_ARRAY(peer_state->peer_macaddr), |
| peer_state->vdev_id, peer_state->peer_state); |
| msg.type = soc_obj->tdls_update_peer_state; |
| msg.reserved = 0; |
| msg.bodyptr = peer_state; |
| |
| status = scheduler_post_message(QDF_MODULE_ID_TDLS, |
| QDF_MODULE_ID_WMA, |
| QDF_MODULE_ID_WMA, &msg); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| tdls_err("scheduler_post_msg failed"); |
| status = QDF_STATUS_E_FAILURE; |
| } |
| |
| return status; |
| } |
| |
| static QDF_STATUS |
| tdls_update_uapsd(struct wlan_objmgr_psoc *psoc, struct wlan_objmgr_vdev *vdev, |
| uint8_t sta_id, uint32_t srvc_int, uint32_t sus_int, |
| uint8_t dir, uint8_t psb, uint32_t delay_interval) |
| { |
| uint8_t i; |
| static const uint8_t ac[AC_PRIORITY_NUM] = {UAPSD_AC_VO, UAPSD_AC_VI, |
| UAPSD_AC_BK, UAPSD_AC_BE}; |
| static const uint8_t tid[AC_PRIORITY_NUM] = {7, 5, 2, 3}; |
| uint32_t vdev_id; |
| |
| struct sta_uapsd_params tdls_uapsd_params; |
| struct sta_uapsd_trig_params tdls_trig_params; |
| struct wlan_objmgr_peer *bsspeer; |
| uint8_t macaddr[QDF_MAC_ADDR_SIZE]; |
| QDF_STATUS status; |
| |
| if (!psb) { |
| tdls_debug("No need to configure auto trigger:psb is 0"); |
| return QDF_STATUS_SUCCESS; |
| } |
| vdev_id = wlan_vdev_get_id(vdev); |
| bsspeer = wlan_vdev_get_bsspeer(vdev); |
| if (!bsspeer) { |
| tdls_err("bss peer is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| wlan_vdev_obj_lock(vdev); |
| qdf_mem_copy(macaddr, |
| wlan_peer_get_macaddr(bsspeer), QDF_MAC_ADDR_SIZE); |
| wlan_vdev_obj_unlock(vdev); |
| |
| tdls_debug("TDLS uapsd id %d, srvc %d, sus %d, dir %d psb %d delay %d", |
| sta_id, srvc_int, sus_int, dir, psb, delay_interval); |
| for (i = 0; i < AC_PRIORITY_NUM; i++) { |
| tdls_uapsd_params.wmm_ac = ac[i]; |
| tdls_uapsd_params.user_priority = tid[i]; |
| tdls_uapsd_params.service_interval = srvc_int; |
| tdls_uapsd_params.delay_interval = delay_interval; |
| tdls_uapsd_params.suspend_interval = sus_int; |
| |
| tdls_trig_params.vdevid = vdev_id; |
| tdls_trig_params.num_ac = 1; |
| tdls_trig_params.auto_triggerparam = &tdls_uapsd_params; |
| |
| qdf_mem_copy(tdls_trig_params.peer_addr, |
| macaddr, QDF_MAC_ADDR_SIZE); |
| status = tgt_tdls_set_uapsd(psoc, &tdls_trig_params); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| tdls_err("Failed to set uapsd for vdev %d, status %d", |
| vdev_id, status); |
| } |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS tdls_process_enable_link(struct tdls_oper_request *req) |
| { |
| struct tdls_peer *peer; |
| struct tdls_vdev_priv_obj *vdev_obj; |
| struct tdls_soc_priv_obj *soc_obj; |
| struct wlan_objmgr_vdev *vdev; |
| uint8_t *mac; |
| struct tdls_peer_update_state *peer_update_param; |
| QDF_STATUS status; |
| uint32_t feature; |
| uint8_t id; |
| |
| vdev = req->vdev; |
| if (!vdev) { |
| tdls_err("NULL vdev object"); |
| qdf_mem_free(req); |
| return QDF_STATUS_E_NULL_VALUE; |
| } |
| |
| /* vdev reference cnt is acquired in ucfg_tdls_oper */ |
| vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); |
| soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); |
| |
| if (!vdev_obj || !soc_obj) { |
| tdls_err("tdls vdev_obj: %pK soc_obj: %pK", vdev_obj, soc_obj); |
| status = QDF_STATUS_E_NULL_VALUE; |
| goto error; |
| } |
| |
| mac = req->peer_addr; |
| peer = tdls_find_peer(vdev_obj, mac); |
| if (!peer) { |
| tdls_err(QDF_MAC_ADDR_STR |
| " not found, ignore NL80211_TDLS_ENABLE_LINK", |
| QDF_MAC_ADDR_ARRAY(mac)); |
| status = QDF_STATUS_E_INVAL; |
| goto error; |
| } |
| |
| tdls_debug("enable link for peer " QDF_MAC_ADDR_STR " link state %d", |
| QDF_MAC_ADDR_ARRAY(mac), peer->link_status); |
| if (!TDLS_STA_INDEX_CHECK(peer->sta_id)) { |
| tdls_err("invalid sta idx %u for " QDF_MAC_ADDR_STR, |
| peer->sta_id, QDF_MAC_ADDR_ARRAY(mac)); |
| status = QDF_STATUS_E_INVAL; |
| goto error; |
| } |
| |
| peer->tdls_support = TDLS_CAP_SUPPORTED; |
| if (TDLS_LINK_CONNECTED != peer->link_status) |
| tdls_set_peer_link_status(peer, TDLS_LINK_CONNECTED, |
| TDLS_LINK_SUCCESS); |
| |
| id = wlan_vdev_get_id(vdev); |
| status = soc_obj->tdls_reg_peer(soc_obj->tdls_peer_context, |
| id, mac, peer->sta_id, |
| peer->qos); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| tdls_err("TDLS register peer fail, status %d", status); |
| goto error; |
| } |
| |
| peer_update_param = qdf_mem_malloc(sizeof(*peer_update_param)); |
| if (!peer_update_param) { |
| tdls_err("memory allocation failed"); |
| status = QDF_STATUS_E_NOMEM; |
| goto error; |
| } |
| |
| tdls_extract_peer_state_param(peer_update_param, peer); |
| |
| status = tdls_wma_update_peer_state(soc_obj, peer_update_param); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| qdf_mem_free(peer_update_param); |
| status = QDF_STATUS_E_PERM; |
| goto error; |
| } |
| |
| tdls_increment_peer_count(soc_obj); |
| feature = soc_obj->tdls_configs.tdls_feature_flags; |
| |
| if (soc_obj->tdls_dp_vdev_update) |
| soc_obj->tdls_dp_vdev_update(&soc_obj->soc, |
| peer->sta_id, |
| soc_obj->tdls_update_dp_vdev_flags, |
| ((peer->link_status == |
| TDLS_LINK_CONNECTED) ? true : false)); |
| |
| tdls_debug("TDLS buffer sta: %d, uapsd_mask %d", |
| TDLS_IS_BUFFER_STA_ENABLED(feature), |
| soc_obj->tdls_configs.tdls_uapsd_mask); |
| |
| if (TDLS_IS_BUFFER_STA_ENABLED(feature) || |
| soc_obj->tdls_configs.tdls_uapsd_mask) |
| tdls_update_uapsd(soc_obj->soc, |
| vdev, peer->sta_id, 0, 0, BI_DIR, 1, |
| soc_obj->tdls_configs.delayed_trig_framint); |
| error: |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); |
| qdf_mem_free(req); |
| |
| return status; |
| } |
| |
| /** |
| * tdls_config_force_peer() - configure an externally controllable TDLS peer |
| * @req: TDLS operation request |
| * |
| * This is not the tdls_process_cmd function. No need to acquire the reference |
| * count, release reference count and free the request, the caller handle it |
| * correctly. |
| * |
| * Return: QDF_STATUS_SUCCESS if success; other values if failed |
| */ |
| static QDF_STATUS tdls_config_force_peer( |
| struct tdls_oper_config_force_peer_request *req) |
| { |
| struct tdls_peer *peer; |
| struct tdls_soc_priv_obj *soc_obj; |
| struct tdls_vdev_priv_obj *vdev_obj; |
| struct wlan_objmgr_pdev *pdev; |
| struct wlan_objmgr_vdev *vdev; |
| const uint8_t *macaddr; |
| uint32_t feature; |
| QDF_STATUS status; |
| struct tdls_peer_update_state *peer_update_param; |
| |
| macaddr = req->peer_addr; |
| tdls_debug("NL80211_TDLS_SETUP for " QDF_MAC_ADDR_STR, |
| QDF_MAC_ADDR_ARRAY(macaddr)); |
| |
| vdev = req->vdev; |
| pdev = wlan_vdev_get_pdev(vdev); |
| vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); |
| soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); |
| if (!pdev || !vdev_obj || !soc_obj) { |
| tdls_err("pdev: %pK, vdev_obj: %pK, soc_obj: %pK", |
| pdev, vdev_obj, soc_obj); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| feature = soc_obj->tdls_configs.tdls_feature_flags; |
| if (!TDLS_IS_EXTERNAL_CONTROL_ENABLED(feature) || |
| !TDLS_IS_IMPLICIT_TRIG_ENABLED(feature)) { |
| tdls_err("TDLS ext ctrl or Imp Trig not enabled, %x", feature); |
| return QDF_STATUS_E_NOSUPPORT; |
| } |
| |
| peer_update_param = qdf_mem_malloc(sizeof(*peer_update_param)); |
| if (!peer_update_param) { |
| tdls_err("memory allocation failed"); |
| return QDF_STATUS_E_NOMEM; |
| } |
| |
| peer = tdls_get_peer(vdev_obj, macaddr); |
| if (!peer) { |
| tdls_err("peer " QDF_MAC_ADDR_STR " does not exist", |
| QDF_MAC_ADDR_ARRAY(macaddr)); |
| status = QDF_STATUS_E_NULL_VALUE; |
| goto error; |
| } |
| status = tdls_set_force_peer(vdev_obj, macaddr, true); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| tdls_err("set force peer failed"); |
| goto error; |
| } |
| |
| /* Update the peer mac to firmware, so firmware could update the |
| * connection table |
| */ |
| peer_update_param->vdev_id = wlan_vdev_get_id(vdev); |
| qdf_mem_copy(peer_update_param->peer_macaddr, |
| macaddr, QDF_MAC_ADDR_SIZE); |
| peer_update_param->peer_state = TDLS_PEER_ADD_MAC_ADDR; |
| |
| status = tdls_wma_update_peer_state(soc_obj, peer_update_param); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| tdls_err("update peer state failed"); |
| goto error; |
| } |
| |
| soc_obj->tdls_external_peer_count++; |
| |
| /* Validate if off channel is DFS channel */ |
| if (wlan_reg_is_dfs_ch(pdev, req->chan)) { |
| tdls_err("Resetting TDLS off-channel from %d to %d", |
| req->chan, WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_DEF); |
| req->chan = WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_DEF; |
| } |
| tdls_set_extctrl_param(peer, req->chan, req->max_latency, req->op_class, |
| req->min_bandwidth); |
| |
| tdls_set_callback(peer, req->callback); |
| |
| tdls_set_ct_mode(soc_obj->soc); |
| if (soc_obj->enable_tdls_connection_tracker) |
| tdls_implicit_enable(vdev_obj); |
| |
| return status; |
| error: |
| qdf_mem_free(peer_update_param); |
| return status; |
| } |
| |
| /** |
| * tdls_process_setup_peer() - process configure an externally |
| * controllable TDLS peer |
| * @req: TDLS operation request |
| * |
| * Return: QDF_STATUS_SUCCESS if success; other values if failed |
| */ |
| QDF_STATUS tdls_process_setup_peer(struct tdls_oper_request *req) |
| { |
| struct tdls_oper_config_force_peer_request peer_req; |
| struct tdls_soc_priv_obj *soc_obj; |
| struct wlan_objmgr_vdev *vdev; |
| QDF_STATUS status; |
| |
| tdls_debug("Configure external TDLS peer " QDF_MAC_ADDR_STR, |
| QDF_MAC_ADDR_ARRAY(req->peer_addr)); |
| |
| /* reference cnt is acquired in ucfg_tdls_oper */ |
| vdev = req->vdev; |
| if (!vdev) { |
| tdls_err("NULL vdev object"); |
| status = QDF_STATUS_E_NULL_VALUE; |
| goto freereq; |
| } |
| |
| qdf_mem_zero(&peer_req, sizeof(peer_req)); |
| peer_req.vdev = vdev; |
| qdf_mem_copy(peer_req.peer_addr, req->peer_addr, QDF_MAC_ADDR_SIZE); |
| |
| soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); |
| if (!soc_obj) { |
| tdls_err("NULL soc object"); |
| status = QDF_STATUS_E_INVAL; |
| goto error; |
| } |
| |
| peer_req.chan = soc_obj->tdls_configs.tdls_pre_off_chan_num; |
| |
| status = tdls_config_force_peer(&peer_req); |
| error: |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); |
| freereq: |
| qdf_mem_free(req); |
| |
| return status; |
| } |
| |
| QDF_STATUS tdls_process_remove_force_peer(struct tdls_oper_request *req) |
| { |
| struct tdls_peer *peer; |
| struct tdls_soc_priv_obj *soc_obj; |
| struct tdls_vdev_priv_obj *vdev_obj; |
| struct wlan_objmgr_vdev *vdev; |
| const uint8_t *macaddr; |
| uint32_t feature; |
| QDF_STATUS status; |
| struct tdls_peer_update_state *peer_update_param; |
| struct tdls_osif_indication ind; |
| |
| macaddr = req->peer_addr; |
| tdls_debug("NL80211_TDLS_TEARDOWN for " QDF_MAC_ADDR_STR, |
| QDF_MAC_ADDR_ARRAY(macaddr)); |
| |
| vdev = req->vdev; |
| if (!vdev) { |
| tdls_err("NULL vdev object"); |
| qdf_mem_free(req); |
| return QDF_STATUS_E_NULL_VALUE; |
| } |
| |
| /* reference cnt is acquired in ucfg_tdls_oper */ |
| vdev_obj = wlan_vdev_get_tdls_vdev_obj(req->vdev); |
| soc_obj = wlan_vdev_get_tdls_soc_obj(req->vdev); |
| if (!soc_obj || !vdev_obj) { |
| tdls_err("soc_obj: %pK, vdev_obj: %pK", soc_obj, vdev_obj); |
| status = QDF_STATUS_E_INVAL; |
| goto error; |
| } |
| |
| feature = soc_obj->tdls_configs.tdls_feature_flags; |
| if (!TDLS_IS_EXTERNAL_CONTROL_ENABLED(feature) || |
| !TDLS_IS_IMPLICIT_TRIG_ENABLED(feature)) { |
| tdls_err("TDLS ext ctrl or Imp Trig not enabled, %x", feature); |
| status = QDF_STATUS_E_NOSUPPORT; |
| goto error; |
| } |
| |
| peer = tdls_find_peer(vdev_obj, macaddr); |
| if (!peer) { |
| tdls_err("peer matching " QDF_MAC_ADDR_STR " not found", |
| QDF_MAC_ADDR_ARRAY(macaddr)); |
| status = QDF_STATUS_E_NULL_VALUE; |
| goto error; |
| } |
| |
| tdls_set_peer_link_status(peer, TDLS_LINK_TEARING, |
| TDLS_LINK_UNSPECIFIED); |
| |
| if (soc_obj->tdls_dp_vdev_update) |
| soc_obj->tdls_dp_vdev_update(&soc_obj->soc, |
| peer->sta_id, |
| soc_obj->tdls_update_dp_vdev_flags, |
| false); |
| |
| if (soc_obj->tdls_event_cb) { |
| qdf_mem_copy(ind.peer_mac, macaddr, QDF_MAC_ADDR_SIZE); |
| ind.vdev = vdev; |
| ind.reason = TDLS_TEARDOWN_PEER_UNSPEC_REASON; |
| soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, |
| TDLS_EVENT_TEARDOWN_REQ, &ind); |
| } |
| |
| status = tdls_set_force_peer(vdev_obj, macaddr, false); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| tdls_err("set force peer failed"); |
| status = QDF_STATUS_E_INVAL; |
| goto error; |
| } |
| |
| if (soc_obj->tdls_external_peer_count) |
| soc_obj->tdls_external_peer_count--; |
| |
| tdls_set_callback(peer, NULL); |
| peer_update_param = qdf_mem_malloc(sizeof(*peer_update_param)); |
| if (!peer_update_param) { |
| tdls_err("memory allocation failed"); |
| status = QDF_STATUS_E_NOMEM; |
| goto error; |
| } |
| |
| peer_update_param->vdev_id = wlan_vdev_get_id(vdev); |
| qdf_mem_copy(peer_update_param->peer_macaddr, |
| macaddr, QDF_MAC_ADDR_SIZE); |
| peer_update_param->peer_state = TDLS_PEER_REMOVE_MAC_ADDR; |
| status = tdls_wma_update_peer_state(soc_obj, peer_update_param); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| qdf_mem_free(peer_update_param); |
| goto error; |
| } |
| tdls_set_ct_mode(soc_obj->soc); |
| if (!soc_obj->enable_tdls_connection_tracker) |
| tdls_implicit_disable(vdev_obj); |
| |
| error: |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); |
| qdf_mem_free(req); |
| |
| return status; |
| } |
| |
| static const char *tdls_evt_to_str(enum tdls_event_msg_type type) |
| { |
| switch (type) { |
| case TDLS_SHOULD_DISCOVER: |
| return "SHOULD_DISCOVER"; |
| case TDLS_SHOULD_TEARDOWN: |
| return "SHOULD_TEARDOWN"; |
| case TDLS_PEER_DISCONNECTED: |
| return "SHOULD_PEER_DISCONNECTED"; |
| case TDLS_CONNECTION_TRACKER_NOTIFY: |
| return "CONNECTION_TRACKER_NOTIFICATION"; |
| default: |
| return "INVALID_TYPE"; |
| } |
| } |
| |
| QDF_STATUS tdls_process_should_discover(struct wlan_objmgr_vdev *vdev, |
| struct tdls_event_info *evt) |
| { |
| struct tdls_soc_priv_obj *soc_obj; |
| struct tdls_vdev_priv_obj *vdev_obj; |
| struct tdls_peer *curr_peer; |
| uint32_t feature; |
| uint16_t type; |
| |
| /*TODO ignore this if any concurrency detected*/ |
| soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); |
| vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); |
| type = evt->message_type; |
| |
| tdls_debug("TDLS %s: " QDF_MAC_ADDR_STR "reason %d", |
| tdls_evt_to_str(type), |
| QDF_MAC_ADDR_ARRAY(evt->peermac.bytes), |
| evt->peer_reason); |
| if (!soc_obj || !vdev_obj) { |
| tdls_err("soc_obj: %pK, vdev_obj: %pK, ignore %s", |
| soc_obj, vdev_obj, tdls_evt_to_str(type)); |
| return QDF_STATUS_E_NULL_VALUE; |
| } |
| if (soc_obj->tdls_nss_switch_in_progress) { |
| tdls_err("TDLS antenna switching, ignore %s", |
| tdls_evt_to_str(type)); |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| curr_peer = tdls_get_peer(vdev_obj, evt->peermac.bytes); |
| if (!curr_peer) { |
| tdls_notice("curr_peer is null"); |
| return QDF_STATUS_E_NULL_VALUE; |
| } |
| |
| if (TDLS_LINK_CONNECTED == curr_peer->link_status) { |
| tdls_err("TDLS link status is connected, ignore"); |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| feature = soc_obj->tdls_configs.tdls_feature_flags; |
| if (TDLS_IS_EXTERNAL_CONTROL_ENABLED(feature) && |
| !curr_peer->is_forced_peer) { |
| tdls_debug("curr_peer is not forced, ignore %s", |
| tdls_evt_to_str(type)); |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| tdls_debug("initiate TDLS setup on %s, ext: %d, force: %d, reason: %d", |
| tdls_evt_to_str(type), |
| TDLS_IS_EXTERNAL_CONTROL_ENABLED(feature), |
| curr_peer->is_forced_peer, evt->peer_reason); |
| vdev_obj->curr_candidate = curr_peer; |
| tdls_implicit_send_discovery_request(vdev_obj); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS tdls_process_should_teardown(struct wlan_objmgr_vdev *vdev, |
| struct tdls_event_info *evt) |
| { |
| struct tdls_soc_priv_obj *soc_obj; |
| struct tdls_vdev_priv_obj *vdev_obj; |
| struct tdls_peer *curr_peer; |
| uint32_t reason; |
| uint16_t type; |
| |
| type = evt->message_type; |
| soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); |
| vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); |
| |
| tdls_debug("TDLS %s: " QDF_MAC_ADDR_STR "reason %d", |
| tdls_evt_to_str(type), |
| QDF_MAC_ADDR_ARRAY(evt->peermac.bytes), evt->peer_reason); |
| |
| if (!soc_obj || !vdev_obj) { |
| tdls_err("soc_obj: %pK, vdev_obj: %pK, ignore %s", |
| soc_obj, vdev_obj, tdls_evt_to_str(type)); |
| return QDF_STATUS_E_NULL_VALUE; |
| } |
| |
| curr_peer = tdls_find_peer(vdev_obj, evt->peermac.bytes); |
| if (!curr_peer) { |
| tdls_notice("curr_peer is null"); |
| return QDF_STATUS_E_NULL_VALUE; |
| } |
| |
| reason = evt->peer_reason; |
| if (TDLS_LINK_CONNECTED == curr_peer->link_status) { |
| tdls_err("%s reason: %d for" QDF_MAC_ADDR_STR, |
| tdls_evt_to_str(type), evt->peer_reason, |
| QDF_MAC_ADDR_ARRAY(evt->peermac.bytes)); |
| if (reason == TDLS_TEARDOWN_RSSI || |
| reason == TDLS_DISCONNECTED_PEER_DELETE || |
| reason == TDLS_TEARDOWN_PTR_TIMEOUT || |
| reason == TDLS_TEARDOWN_NO_RSP) |
| reason = TDLS_TEARDOWN_PEER_UNREACHABLE; |
| else |
| reason = TDLS_TEARDOWN_PEER_UNSPEC_REASON; |
| |
| tdls_indicate_teardown(vdev_obj, curr_peer, reason); |
| } else { |
| tdls_err("TDLS link is not connected, ignore %s", |
| tdls_evt_to_str(type)); |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS tdls_process_connection_tracker_notify(struct wlan_objmgr_vdev *vdev, |
| struct tdls_event_info *evt) |
| { |
| struct tdls_soc_priv_obj *soc_obj; |
| struct tdls_vdev_priv_obj *vdev_obj; |
| uint16_t type; |
| |
| type = evt->message_type; |
| soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); |
| vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); |
| |
| if (!soc_obj || !vdev_obj) { |
| tdls_err("soc_obj: %pK, vdev_obj: %pK, ignore %s", |
| soc_obj, vdev_obj, tdls_evt_to_str(type)); |
| return QDF_STATUS_E_NULL_VALUE; |
| } |
| |
| /*TODO connection tracker update*/ |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * tdls_process_set_responder() - Set/clear TDLS peer's responder role |
| * @set_req: set responder request |
| * |
| * Return: 0 for success or -EINVAL otherwise |
| */ |
| static |
| int tdls_process_set_responder(struct tdls_set_responder_req *set_req) |
| { |
| struct tdls_peer *curr_peer; |
| struct tdls_vdev_priv_obj *tdls_vdev; |
| |
| tdls_vdev = wlan_vdev_get_tdls_vdev_obj(set_req->vdev); |
| if (!tdls_vdev) { |
| tdls_err("tdls vdev obj is NULL"); |
| return -EINVAL; |
| } |
| curr_peer = tdls_get_peer(tdls_vdev, set_req->peer_mac); |
| if (curr_peer == NULL) { |
| tdls_err("curr_peer is NULL"); |
| return -EINVAL; |
| } |
| |
| curr_peer->is_responder = set_req->responder; |
| return 0; |
| } |
| |
| |
| /** |
| * tdls_set_responder() - Set/clear TDLS peer's responder role |
| * @set_req: set responder request |
| * |
| * Return: 0 for success or -EINVAL otherwise |
| */ |
| int tdls_set_responder(struct tdls_set_responder_req *set_req) |
| { |
| QDF_STATUS status; |
| |
| if (!set_req || !set_req->vdev) { |
| tdls_err("Invalid input params %pK", set_req); |
| return -EINVAL; |
| } |
| |
| status = wlan_objmgr_vdev_try_get_ref(set_req->vdev, WLAN_TDLS_NB_ID); |
| if (QDF_STATUS_SUCCESS != status) { |
| tdls_err("vdev object is deleted"); |
| return -EINVAL; |
| } |
| |
| status = tdls_process_set_responder(set_req); |
| |
| wlan_objmgr_vdev_release_ref(set_req->vdev, WLAN_TDLS_NB_ID); |
| qdf_mem_free(set_req); |
| return status; |
| } |
| |
| static int tdls_teardown_links(struct tdls_soc_priv_obj *soc_obj, uint32_t mode) |
| { |
| uint8_t staidx; |
| struct tdls_peer *curr_peer; |
| struct tdls_conn_info *conn_rec; |
| int ret = 0; |
| |
| conn_rec = soc_obj->tdls_conn_info; |
| for (staidx = 0; staidx < soc_obj->max_num_tdls_sta; staidx++) { |
| if (conn_rec[staidx].sta_id == INVALID_TDLS_PEER_ID) |
| continue; |
| |
| curr_peer = tdls_find_all_peer(soc_obj, |
| conn_rec[staidx].peer_mac.bytes); |
| if (!curr_peer) |
| continue; |
| |
| /* if supported only 1x1, skip it */ |
| if (curr_peer->spatial_streams == HW_MODE_SS_1x1) |
| continue; |
| |
| tdls_debug("Indicate TDLS teardown (staId %d)", |
| curr_peer->sta_id); |
| tdls_indicate_teardown(curr_peer->vdev_priv, curr_peer, |
| TDLS_TEARDOWN_PEER_UNSPEC_REASON); |
| |
| soc_obj->tdls_teardown_peers_cnt++; |
| } |
| |
| if (soc_obj->tdls_teardown_peers_cnt >= 1) { |
| soc_obj->tdls_nss_switch_in_progress = true; |
| tdls_debug("TDLS peers to be torn down = %d", |
| soc_obj->tdls_teardown_peers_cnt); |
| |
| /* set the antenna switch transition mode */ |
| if (mode == HW_MODE_SS_1x1) { |
| soc_obj->tdls_nss_transition_mode = |
| TDLS_NSS_TRANSITION_S_2x2_to_1x1; |
| ret = -EAGAIN; |
| } else { |
| soc_obj->tdls_nss_transition_mode = |
| TDLS_NSS_TRANSITION_S_1x1_to_2x2; |
| ret = 0; |
| } |
| tdls_debug("TDLS teardown for antenna switch operation starts"); |
| } |
| |
| return ret; |
| } |
| |
| QDF_STATUS tdls_process_antenna_switch(struct tdls_antenna_switch_request *req) |
| { |
| QDF_STATUS status; |
| struct tdls_soc_priv_obj *soc_obj; |
| struct tdls_vdev_priv_obj *vdev_obj; |
| struct wlan_objmgr_vdev *vdev = NULL; |
| uint32_t vdev_nss; |
| int ant_switch_state = 0; |
| uint32_t vdev_id; |
| enum QDF_OPMODE opmode; |
| uint8_t channel; |
| struct tdls_osif_indication ind; |
| |
| if (!req) { |
| tdls_err("null req"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| vdev = req->vdev; |
| if (!vdev) { |
| tdls_err("null vdev"); |
| qdf_mem_free(req); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| status = tdls_get_vdev_objects(vdev, &vdev_obj, &soc_obj); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| tdls_err("can't get vdev_obj & soc_obj"); |
| goto get_obj_err; |
| } |
| |
| if (soc_obj->connected_peer_count == 0) |
| goto ant_sw_done; |
| |
| if (soc_obj->tdls_nss_switch_in_progress) { |
| if (!soc_obj->tdls_nss_teardown_complete) { |
| tdls_err("TDLS antenna switch is in progress"); |
| goto ant_sw_in_progress; |
| } else { |
| goto ant_sw_done; |
| } |
| } |
| |
| vdev_id = wlan_vdev_get_id(vdev); |
| opmode = wlan_vdev_mlme_get_opmode(vdev); |
| channel = policy_mgr_get_channel(soc_obj->soc, |
| policy_mgr_convert_device_mode_to_qdf_type(opmode), |
| &vdev_id); |
| |
| /* Check supported nss for TDLS, if is 1x1, no need to teardown links */ |
| if (WLAN_REG_IS_24GHZ_CH(channel)) |
| vdev_nss = soc_obj->tdls_configs.tdls_vdev_nss_2g; |
| else |
| vdev_nss = soc_obj->tdls_configs.tdls_vdev_nss_5g; |
| |
| if (vdev_nss == HW_MODE_SS_1x1) { |
| tdls_debug("Supported NSS is 1x1, no need to teardown TDLS links"); |
| goto ant_sw_done; |
| } |
| |
| if (tdls_teardown_links(soc_obj, req->mode) == 0) |
| goto ant_sw_done; |
| |
| ant_sw_in_progress: |
| ant_switch_state = -EAGAIN; |
| ant_sw_done: |
| if (soc_obj->tdls_event_cb) { |
| ind.vdev = vdev; |
| ind.status = ant_switch_state; |
| soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, |
| TDLS_EVENT_ANTENNA_SWITCH, &ind); |
| } |
| |
| if (soc_obj->tdls_nss_switch_in_progress && |
| soc_obj->tdls_nss_teardown_complete) { |
| soc_obj->tdls_nss_switch_in_progress = false; |
| soc_obj->tdls_nss_teardown_complete = false; |
| } |
| tdls_debug("tdls_nss_switch_in_progress: %d tdls_nss_teardown_complete: %d", |
| soc_obj->tdls_nss_switch_in_progress, |
| soc_obj->tdls_nss_teardown_complete); |
| |
| get_obj_err: |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); |
| qdf_mem_free(req); |
| |
| return status; |
| } |
| |
| QDF_STATUS tdls_antenna_switch_flush_callback(struct scheduler_msg *msg) |
| { |
| struct tdls_antenna_switch_request *req; |
| |
| if (!msg || !msg->bodyptr) { |
| tdls_err("msg: 0x%pK", msg); |
| return QDF_STATUS_E_NULL_VALUE; |
| } |
| req = msg->bodyptr; |
| wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); |
| qdf_mem_free(req); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| void wlan_tdls_offchan_parms_callback(struct wlan_objmgr_vdev *vdev) |
| { |
| if (!vdev) { |
| tdls_err("vdev is NULL"); |
| return; |
| } |
| |
| wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); |
| } |
| |
| int tdls_process_set_offchannel(struct tdls_set_offchannel *req) |
| { |
| int status; |
| struct tdls_vdev_priv_obj *tdls_vdev_obj; |
| struct tdls_soc_priv_obj *tdls_soc_obj; |
| |
| if (tdls_get_vdev_objects(req->vdev, &tdls_vdev_obj, &tdls_soc_obj) != |
| QDF_STATUS_SUCCESS) { |
| status = -ENOTSUPP; |
| goto free; |
| } |
| |
| tdls_debug("TDLS offchannel to be configured %d", req->offchannel); |
| |
| if (req->offchannel) |
| status = tdls_set_tdls_offchannel(tdls_soc_obj, |
| req->offchannel); |
| else |
| status = -ENOTSUPP; |
| |
| free: |
| |
| if (req->callback) |
| req->callback(req->vdev); |
| qdf_mem_free(req); |
| |
| return status; |
| } |
| |
| int tdls_process_set_offchan_mode(struct tdls_set_offchanmode *req) |
| { |
| int status; |
| |
| tdls_debug("TDLS offchan mode to be configured %d", req->offchan_mode); |
| status = tdls_set_tdls_offchannelmode(req->vdev, req->offchan_mode); |
| |
| if (req->callback) |
| req->callback(req->vdev); |
| qdf_mem_free(req); |
| |
| return status; |
| } |
| |
| int tdls_process_set_secoffchanneloffset( |
| struct tdls_set_secoffchanneloffset *req) |
| { |
| int status; |
| struct tdls_vdev_priv_obj *tdls_vdev_obj; |
| struct tdls_soc_priv_obj *tdls_soc_obj; |
| |
| if (tdls_get_vdev_objects(req->vdev, &tdls_vdev_obj, &tdls_soc_obj) != |
| QDF_STATUS_SUCCESS) { |
| status = -ENOTSUPP; |
| goto free; |
| } |
| |
| tdls_debug("TDLS offchannel offset to be configured %d", |
| req->offchan_offset); |
| status = tdls_set_tdls_secoffchanneloffset(tdls_soc_obj, |
| req->offchan_offset); |
| |
| free: |
| |
| if (req->callback) |
| req->callback(req->vdev); |
| qdf_mem_free(req); |
| |
| return status; |
| } |