diff --git a/umac/tdls/core/src/wlan_tdls_cmds_process.c b/umac/tdls/core/src/wlan_tdls_cmds_process.c index 7a6aa6cb4953..12dde2563df9 100644 --- a/umac/tdls/core/src/wlan_tdls_cmds_process.c +++ b/umac/tdls/core/src/wlan_tdls_cmds_process.c @@ -21,3 +21,1151 @@ * * TDLS north bound commands implementation */ +#include +#include "wlan_tdls_main.h" +#include "wlan_tdls_peer.h" +#include "wlan_tdls_cmds_process.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 + * + * Return: None. + */ +static 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_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"; + } +} + +static 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; + + wlan_vdev_obj_lock(vdev); + addstareq->session_id = wlan_vdev_get_id(vdev); + peer = wlan_vdev_get_bsspeer(vdev); + if (!peer) { + wlan_vdev_obj_unlock(vdev); + tdls_err("bss peer is NULL"); + status = QDF_STATUS_E_INVAL; + goto error; + } + wlan_vdev_obj_unlock(vdev); + 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_ADDRESS_STR, + QDF_MAC_ADDR_ARRAY(addstareq->peermac.bytes)); + msg.type = soc_obj->tdls_add_sta_req; + msg.bodyptr = addstareq; + status = scheduler_post_msg(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; + + wlan_vdev_obj_lock(vdev); + delstareq->session_id = wlan_vdev_get_id(vdev); + peer = wlan_vdev_get_bsspeer(vdev); + if (!peer) { + wlan_vdev_obj_unlock(vdev); + tdls_err("bss peer is NULL"); + status = QDF_STATUS_E_INVAL; + goto error; + } + wlan_vdev_obj_unlock(vdev); + 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_ADDRESS_STR, + QDF_MAC_ADDR_ARRAY(delstareq->peermac.bytes)); + msg.type = soc_obj->tdls_del_sta_req; + msg.bodyptr = delstareq; + status = scheduler_post_msg(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; + + wlan_vdev_obj_lock(vdev); + addstareq->session_id = wlan_vdev_get_id(vdev); + peer = wlan_vdev_get_bsspeer(vdev); + if (!peer) { + wlan_vdev_obj_unlock(vdev); + tdls_err("bss peer is NULL"); + status = QDF_STATUS_E_INVAL; + goto error; + } + wlan_vdev_obj_unlock(vdev); + 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_ADDRESS_STR, + QDF_MAC_ADDR_ARRAY(addstareq->peermac.bytes)); + + msg.type = soc_obj->tdls_add_sta_req; + msg.bodyptr = addstareq; + status = scheduler_post_msg(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: %p", 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: %p", 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: %p", 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: %p, vdev_obj: %p", 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_ADDRESS_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_ADDRESS_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_ADDRESS_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: %p, reason: %d", cmd, reason); + return QDF_STATUS_E_NULL_VALUE; + } + + req = cmd->umac_cmd; + tdls_debug("reason: %d, req %p", 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; +} + +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: %p", 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 = WAIT_TIME_TDLS_ADD_STA; + cmd.vdev = vdev; + + ser_cmd_status = wlan_serialization_request(&cmd); + tdls_debug("req: 0x%p 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: %p, vdev_obj: %p", 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_ADDRESS_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_ADDRESS_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_ADDRESS_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_ADDRESS_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: %p, reason: %d", cmd, reason); + return QDF_STATUS_E_NULL_VALUE; + } + + req = cmd->umac_cmd; + tdls_debug("reason: %d, req %p", 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: %p", 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 = WAIT_TIME_TDLS_ADD_STA; + cmd.vdev = req->vdev; + + ser_cmd_status = wlan_serialization_request(&cmd); + tdls_debug("req: 0x%p 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: %p, reason: %d", cmd, reason); + return QDF_STATUS_E_NULL_VALUE; + } + + req = cmd->umac_cmd; + tdls_debug("reason: %d, req %p", 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; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!req || !req->vdev) { + tdls_err("req: %p", req); + status = QDF_STATUS_E_INVAL; + goto error; + } + vdev = req->vdev; + 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 = WAIT_TIME_TDLS_DEL_STA; + cmd.vdev = vdev; + + ser_cmd_status = wlan_serialization_request(&cmd); + tdls_debug("req: 0x%p 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); + 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_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:%p, vdev object:%p", 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 (0 == 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_ADDRESS_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; + goto cmddone; + } + + tdls_set_signature(vdev_obj, rsp->peermac.bytes, + rsp->ucast_sig); + } 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_ADDRESS_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:%p, vdev object:%p", 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_ADDRESS_STR " status is %d", + QDF_MAC_ADDR_ARRAY(macaddr), + curr_peer->link_status); + wlan_vdev_obj_lock(vdev); + id = wlan_vdev_get_id(vdev); + wlan_vdev_obj_unlock(vdev); + if (TDLS_IS_CONNECTED(curr_peer)) { + soc_obj->tdls_dereg_tl_peer( + soc_obj->tdls_tl_peer_data, + id, curr_peer->sta_id); + tdls_decrement_peer_count(soc_obj); + } else if (TDLS_LINK_CONNECTING == + curr_peer->link_status) { + soc_obj->tdls_dereg_tl_peer( + soc_obj->tdls_tl_peer_data, + id, curr_peer->sta_id); + } + } + tdls_reset_peer(vdev_obj, macaddr); + conn_rec[sta_idx].sta_id = 0; + 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; +} diff --git a/umac/tdls/core/src/wlan_tdls_cmds_process.h b/umac/tdls/core/src/wlan_tdls_cmds_process.h index a90931846aba..253403f55047 100644 --- a/umac/tdls/core/src/wlan_tdls_cmds_process.h +++ b/umac/tdls/core/src/wlan_tdls_cmds_process.h @@ -21,3 +21,177 @@ * * TDLS north bound commands include file */ + +#ifndef _WLAN_TDLS_CMDS_PROCESS_H_ +#define _WLAN_TDLS_CMDS_PROCESS_H_ + +/** + * enum tdls_add_oper - add peer type + * @TDLS_OPER_NONE: none + * @TDLS_OPER_ADD: add new peer + * @TDLS_OPER_UPDATE: used to update peer + */ +enum tdls_add_oper { + TDLS_OPER_NONE, + TDLS_OPER_ADD, + TDLS_OPER_UPDATE +}; + +/** + * struct tdls_add_sta_req - TDLS request struct TDLS module --> PE + * same as struct tSirTdlsAddStaReq; + * @message_type: eWNI_SME_TDLS_ADD_STA_REQ + * @length: message length + * @session_id: session id + * @transaction_id: transaction id for cmd + * @bssid: bssid + * @tdls_oper: add peer type + * @peermac: MAC address for TDLS peer + * @capability: mac capability as sSirMacCapabilityInfo + * @extn_capability: extent capability + * @supported_rates_length: rates length + * @supported_rates: supported rates + * @htcap_present: ht capability present + * @ht_cap: ht capability + * @vhtcap_present: vht capability present + * @vht_cap: vht capability + * @uapsd_queues: uapsd queue as sSirMacQosInfoStation + * @max_sp: maximum service period + */ +struct tdls_add_sta_req { + uint16_t message_type; + uint16_t length; + uint8_t session_id; + uint16_t transaction_id; + struct qdf_mac_addr bssid; + enum tdls_add_oper tdls_oper; + struct qdf_mac_addr peermac; + uint16_t capability; + uint8_t extn_capability[WLAN_MAC_MAX_EXTN_CAP]; + uint8_t supported_rates_length; + uint8_t supported_rates[WLAN_MAC_MAX_SUPP_RATES]; + uint8_t htcap_present; + struct htcap_cmn_ie ht_cap; + uint8_t vhtcap_present; + struct vhtcap vht_cap; + uint8_t uapsd_queues; + uint8_t max_sp; +}; + +/** + * struct tdls_add_sta_rsp - TDLS Response struct PE --> TDLS module + * same as struct sSirTdlsAddStaRsp + * @message_type: message type eWNI_SME_TDLS_ADD_STA_RSP + * @length: message length + * @status_code: status code as tSirResultCodes + * @peermac: MAC address of the TDLS peer + * @session_id: session id + * @sta_id: sta id + * @sta_type: sta type + * @ucast_sig: unicast signature + * @bcast_sig: broadcast signature + * @tdls_oper: add peer type + * @psoc: soc object + */ +struct tdls_add_sta_rsp { + uint16_t message_type; + uint16_t length; + QDF_STATUS status_code; + struct qdf_mac_addr peermac; + uint8_t session_id; + uint16_t sta_id; + uint16_t sta_type; + uint8_t ucast_sig; + uint8_t bcast_sig; + enum tdls_add_oper tdls_oper; + struct wlan_objmgr_psoc *psoc; +}; + +/** + * struct tdls_del_sta_req - TDLS Request struct TDLS module --> PE + * same as sSirTdlsDelStaReq + * @message_type: message type eWNI_SME_TDLS_DEL_STA_REQ + * @length: message length + * @session_id: session id + * @transaction_id: transaction id for cmd + * @bssid: bssid + * @peermac: MAC address of the TDLS peer + */ +struct tdls_del_sta_req { + uint16_t message_type; + uint16_t length; + uint8_t session_id; + uint16_t transaction_id; + struct qdf_mac_addr bssid; + struct qdf_mac_addr peermac; +}; + +/** + * struct tdls_del_sta_rsp - TDLS Response struct PE --> TDLS module + * same as sSirTdlsDelStaRsp + * @message_type: message type eWNI_SME_TDLS_DEL_STA_RSP + * @length: message length + * @session_id: session id + * @status_code: status code as tSirResultCodes + * @peermac: MAC address of the TDLS peer + * @sta_id: sta id + * @psoc: soc object + */ +struct tdls_del_sta_rsp { + uint16_t message_type; + uint16_t length; + uint8_t session_id; + QDF_STATUS status_code; + struct qdf_mac_addr peermac; + uint16_t sta_id; + struct wlan_objmgr_psoc *psoc; +}; + +/** + * tdls_process_add_peer() - add TDLS peer + * @req: TDLS add peer request + * + * Return: QDF_STATUS_SUCCESS if success; other value if failed + */ +QDF_STATUS tdls_process_add_peer(struct tdls_add_peer_request *req); + +/** + * tdls_process_del_peer() - del TDLS peer + * @req: TDLS del peer request + * + * Return: QDF_STATUS_SUCCESS if success; other value if failed + */ +QDF_STATUS tdls_process_del_peer(struct tdls_oper_request *req); + +/** + * tdls_process_update_peer() - update TDLS peer + * @req: TDLS update peer request + * + * Return: QDF_STATUS_SUCCESS if success; other value if failed + */ +QDF_STATUS tdls_process_update_peer(struct tdls_update_peer_request *req); + +/** + * tdls_pe_del_peer() - send TDLS delete peer request to PE + * @req: TDLS delete peer request + * + * Return: QDF status + */ +QDF_STATUS tdls_pe_del_peer(struct tdls_del_peer_request *req); + +/** + * tdls_process_add_peer_rsp() - handle response for add or update TDLS peer + * @rsp: TDLS add peer response + * + * Return: QDF status + */ +QDF_STATUS tdls_process_add_peer_rsp(struct tdls_add_sta_rsp *rsp); + +/** + * tdls_process_add_peer_rsp() - handle response for delete TDLS peer + * @rsp: TDLS delete peer response + * + * Return: QDF status + */ +QDF_STATUS tdls_process_del_peer_rsp(struct tdls_del_sta_rsp *rsp); +#endif diff --git a/umac/tdls/core/src/wlan_tdls_main.c b/umac/tdls/core/src/wlan_tdls_main.c index eef8915fb300..ab4a5e5c9aa7 100644 --- a/umac/tdls/core/src/wlan_tdls_main.c +++ b/umac/tdls/core/src/wlan_tdls_main.c @@ -23,6 +23,8 @@ */ #include "wlan_tdls_main.h" +#include "wlan_tdls_cmds_process.h" +#include "wlan_tdls_peer.h" QDF_STATUS tdls_psoc_obj_create_notification(struct wlan_objmgr_psoc *psoc, void *arg_list) @@ -31,7 +33,7 @@ QDF_STATUS tdls_psoc_obj_create_notification(struct wlan_objmgr_psoc *psoc, struct tdls_soc_priv_obj *tdls_soc_obj; tdls_soc_obj = qdf_mem_malloc(sizeof(*tdls_soc_obj)); - if (tdls_soc_obj == NULL) { + if (!tdls_soc_obj) { tdls_err("Failed to allocate memory for tdls object"); return QDF_STATUS_E_NOMEM; } @@ -61,7 +63,7 @@ QDF_STATUS tdls_psoc_obj_destroy_notification(struct wlan_objmgr_psoc *psoc, tdls_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_TDLS); - if (tdls_soc_obj == NULL) { + if (!tdls_soc_obj) { tdls_err("Failed to get tdls obj in psoc"); return QDF_STATUS_E_FAILURE; } @@ -78,19 +80,53 @@ QDF_STATUS tdls_psoc_obj_destroy_notification(struct wlan_objmgr_psoc *psoc, return status; } -static void tdls_vdev_init(struct tdls_vdev_priv_obj *vdev) +static QDF_STATUS tdls_vdev_init(struct tdls_vdev_priv_obj *vdev_obj) { uint8_t i; + struct tdls_config_params *config; + struct tdls_user_config *user_config; + struct tdls_soc_priv_obj *soc_obj; - for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { - qdf_list_create(&vdev->peer_list[i], - WLAN_TDLS_PEER_SUB_LIST_SIZE); + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev); + if (!soc_obj) { + tdls_err("tdls soc obj NULL"); + return QDF_STATUS_E_FAILURE; } - qdf_mc_timer_init(&vdev->peer_update_timer, QDF_TIMER_TYPE_SW, - NULL, vdev); - qdf_mc_timer_init(&vdev->peer_update_timer, QDF_TIMER_TYPE_SW, - NULL, vdev); + config = &vdev_obj->threshold_config; + user_config = &soc_obj->tdls_configs; + config->tx_period_t = user_config->tdls_tx_states_period; + config->tx_packet_n = user_config->tdls_tx_pkt_threshold; + config->discovery_tries_n = user_config->tdls_max_discovery_attempt; + config->idle_timeout_t = user_config->tdls_idle_timeout; + config->idle_packet_n = user_config->tdls_idle_pkt_threshold; + config->rssi_trigger_threshold = + user_config->tdls_rssi_trigger_threshold; + config->rssi_teardown_threshold = + user_config->tdls_rssi_teardown_threshold; + config->rssi_delta = user_config->tdls_rssi_delta; + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + qdf_list_create(&vdev_obj->peer_list[i], + WLAN_TDLS_PEER_SUB_LIST_SIZE); + } + qdf_mc_timer_init(&vdev_obj->peer_update_timer, QDF_TIMER_TYPE_SW, + NULL, vdev_obj); + qdf_mc_timer_init(&vdev_obj->peer_discovery_timer, QDF_TIMER_TYPE_SW, + NULL, vdev_obj); + return QDF_STATUS_SUCCESS; +} + +static void tdls_vdev_deinit(struct tdls_vdev_priv_obj *vdev_obj) +{ + qdf_mc_timer_stop(&vdev_obj->peer_update_timer); + qdf_mc_timer_stop(&vdev_obj->peer_discovery_timer); + + qdf_mc_timer_destroy(&vdev_obj->peer_update_timer); + qdf_mc_timer_destroy(&vdev_obj->peer_discovery_timer); + + tdls_peer_idle_timers_destroy(vdev_obj); + tdls_free_peer_list(vdev_obj); } QDF_STATUS tdls_vdev_obj_create_notification(struct wlan_objmgr_vdev *vdev, @@ -107,14 +143,11 @@ QDF_STATUS tdls_vdev_obj_create_notification(struct wlan_objmgr_vdev *vdev, /* TODO: Add concurrency check */ tdls_vdev_obj = qdf_mem_malloc(sizeof(*tdls_vdev_obj)); - if (tdls_vdev_obj == NULL) { + if (!tdls_vdev_obj) { tdls_err("Failed to allocate memory for tdls vdev object"); return QDF_STATUS_E_NOMEM; } - tdls_vdev_obj->vdev = vdev; - tdls_vdev_init(tdls_vdev_obj); - status = wlan_objmgr_vdev_component_obj_attach(vdev, WLAN_UMAC_COMP_TDLS, (void *)tdls_vdev_obj, @@ -122,10 +155,15 @@ QDF_STATUS tdls_vdev_obj_create_notification(struct wlan_objmgr_vdev *vdev, if (QDF_IS_STATUS_ERROR(status)) { tdls_err("Failed to attach vdev tdls component"); qdf_mem_free(tdls_vdev_obj); + goto out; } + tdls_vdev_obj->vdev = vdev; + status = tdls_vdev_init(tdls_vdev_obj); + if (QDF_IS_STATUS_ERROR(status)) + goto out; tdls_notice("tdls object attach to vdev successfully"); - +out: return status; } @@ -142,7 +180,7 @@ QDF_STATUS tdls_vdev_obj_destroy_notification(struct wlan_objmgr_vdev *vdev, tdls_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, WLAN_UMAC_COMP_TDLS); - if (tdls_vdev_obj == NULL) { + if (!tdls_vdev_obj) { tdls_err("Failed to get tdls vdev object"); return QDF_STATUS_E_FAILURE; } @@ -153,7 +191,47 @@ QDF_STATUS tdls_vdev_obj_destroy_notification(struct wlan_objmgr_vdev *vdev, if (QDF_IS_STATUS_ERROR(status)) tdls_err("Failed to detach vdev tdls component"); + tdls_vdev_deinit(tdls_vdev_obj); qdf_mem_free(tdls_vdev_obj); return status; } + +QDF_STATUS tdls_process_cmd(struct scheduler_msg *msg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!msg || !msg->bodyptr) { + tdls_err("msg: 0x%p, bodyptr: 0x%p", msg, msg->bodyptr); + QDF_ASSERT(0); + return QDF_STATUS_E_NULL_VALUE; + } + + switch (msg->type) { + case TDLS_CMD_TX_ACTION: + break; + case TDLS_CMD_ADD_STA: + tdls_process_add_peer(msg->bodyptr); + break; + case TDLS_CMD_CHANGE_STA: + tdls_process_update_peer(msg->bodyptr); + break; + case TDLS_CMD_ENABLE_LINK: + break; + case TDLS_CMD_DISABLE_LINK: + tdls_process_del_peer(msg->bodyptr); + break; + case TDLS_CMD_CONFIG_FORCE_PEER: + break; + case TDLS_CMD_REMOVE_FORCE_PEER: + break; + case TDLS_CMD_STATS_UPDATE: + break; + case TDLS_CMD_CONFIG_UPDATE: + break; + default: + break; + } + + return status; +} diff --git a/umac/tdls/core/src/wlan_tdls_main.h b/umac/tdls/core/src/wlan_tdls_main.h index b85c74cc5f57..4d4d5f2bbd04 100644 --- a/umac/tdls/core/src/wlan_tdls_main.h +++ b/umac/tdls/core/src/wlan_tdls_main.h @@ -39,6 +39,17 @@ #define ENA_TDLS_BUFFER_STA (1 << 1) /* TDLS Buffer STA support */ #define ENA_TDLS_SLEEP_STA (1 << 2) /* TDLS Sleep STA support */ +#define BW_20_OFFSET_BIT 0 +#define BW_40_OFFSET_BIT 1 +#define BW_80_OFFSET_BIT 2 +#define BW_160_OFFSET_BIT 3 + +#define TDLS_SEC_OFFCHAN_OFFSET_0 0 +#define TDLS_SEC_OFFCHAN_OFFSET_40PLUS 40 +#define TDLS_SEC_OFFCHAN_OFFSET_40MINUS (-40) +#define TDLS_SEC_OFFCHAN_OFFSET_80 80 +#define TDLS_SEC_OFFCHAN_OFFSET_160 160 + #define tdls_log(level, args...) \ QDF_TRACE(QDF_MODULE_ID_TDLS, level, ## args) #define tdls_logfl(level, format, args...) \ @@ -163,13 +174,20 @@ struct tdls_set_state_info { * @tx_ack_cnf_cb_data: user data to tdls_tx_cnf_cb * @tdls_event_cb: tdls event callback * @tdls_evt_cb_data: tdls event user data + * @tdls_reg_tl_peer: callback to register the TDLS peer with TL + * @tdls_dereg_tl_peer: callback to unregister the TDLS peer + * @tdls_tl_peer_data: userdata for register/deregister TDLS peer * @tx_q_ack: queue for tx frames waiting for ack * @tdls_con_cap: tdls concurrency support + * @tdls_send_mgmt_req: store eWNI_SME_TDLS_SEND_MGMT_REQ value + * @tdls_add_sta_req: store eWNI_SME_TDLS_ADD_STA_REQ value + * @tdls_del_sta_req: store eWNI_SME_TDLS_DEL_STA_REQ value + * @tdls_update_peer_state: store WMA_UPDATE_TDLS_PEER_STATE value */ struct tdls_soc_priv_obj { struct wlan_objmgr_psoc *soc; - enum tdls_support_mode tdls_current_mode; - enum tdls_support_mode tdls_user_config_mode; + enum tdls_feature_mode tdls_current_mode; + enum tdls_feature_mode tdls_user_config_mode; struct tdls_conn_info tdls_conn_info[WLAN_TDLS_STA_MAX_NUM]; struct tdls_user_config tdls_configs; uint16_t max_num_tdls_sta; @@ -188,8 +206,15 @@ struct tdls_soc_priv_obj { void *tx_ack_cnf_cb_data; tdls_evt_callback tdls_event_cb; void *tdls_evt_cb_data; + tdls_register_tl_peer_callback tdls_reg_tl_peer; + tdls_deregister_tl_peer_callback tdls_dereg_tl_peer; + void *tdls_tl_peer_data; qdf_list_t tx_q_ack; enum tdls_conc_cap tdls_con_cap; + uint16_t tdls_send_mgmt_req; + uint16_t tdls_add_sta_req; + uint16_t tdls_del_sta_req; + uint16_t tdls_update_peer_state; }; /** @@ -225,6 +250,12 @@ struct tdls_vdev_priv_obj { qdf_list_t tx_queue; }; +/** + * struct tdls_peer_mlme_info - tdls peer mlme info + **/ +struct tdls_peer_mlme_info { +}; + /** * struct tdls_peer - tdls peer data * @node: node @@ -266,7 +297,7 @@ struct tdls_peer { uint16_t sta_id; int8_t rssi; enum tdls_peer_capab tdls_support; - enum tdls_link_status link_status; + enum tdls_link_state link_status; uint8_t signature; uint8_t is_responder; uint8_t discovery_processed; @@ -288,9 +319,10 @@ struct tdls_peer { qdf_mc_timer_t peer_idle_timer; bool is_peer_idle_timer_initialised; uint8_t spatial_streams; - enum tdls_link_reason reason; + enum tdls_link_state_reason reason; tdls_state_change_callback state_change_notification; uint8_t qos; + struct tdls_peer_mlme_info *tdls_info; }; /** @@ -302,12 +334,29 @@ struct tdls_peer { static inline struct tdls_soc_priv_obj * wlan_vdev_get_tdls_soc_obj(struct wlan_objmgr_vdev *vdev) { - struct wlan_objmgr_psoc *psoc = - wlan_pdev_get_psoc(wlan_vdev_get_pdev(vdev)); + struct wlan_objmgr_psoc *psoc; + struct tdls_soc_priv_obj *soc_obj; - return (struct tdls_soc_priv_obj *) + if (!vdev) { + tdls_err("NULL vdev"); + return NULL; + } + + wlan_vdev_obj_lock(vdev); + psoc = wlan_vdev_get_psoc(vdev); + wlan_vdev_obj_unlock(vdev); + if (!psoc) { + tdls_err("can't get psoc"); + return NULL; + } + + wlan_psoc_obj_lock(psoc); + soc_obj = (struct tdls_soc_priv_obj *) wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_TDLS); + wlan_psoc_obj_unlock(psoc); + + return soc_obj; } /** @@ -319,9 +368,18 @@ wlan_vdev_get_tdls_soc_obj(struct wlan_objmgr_vdev *vdev) static inline struct tdls_soc_priv_obj * wlan_psoc_get_tdls_soc_obj(struct wlan_objmgr_psoc *psoc) { - return (struct tdls_soc_priv_obj *) + struct tdls_soc_priv_obj *soc_obj; + if (!psoc) { + tdls_err("NULL psoc"); + return NULL; + } + wlan_psoc_obj_lock(psoc); + soc_obj = (struct tdls_soc_priv_obj *) wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_TDLS); + wlan_psoc_obj_unlock(psoc); + + return soc_obj; } /** @@ -333,22 +391,33 @@ wlan_psoc_get_tdls_soc_obj(struct wlan_objmgr_psoc *psoc) static inline struct tdls_vdev_priv_obj * wlan_vdev_get_tdls_vdev_obj(struct wlan_objmgr_vdev *vdev) { - return (struct tdls_vdev_priv_obj *) + struct tdls_vdev_priv_obj *vdev_obj; + + if (!vdev) { + tdls_err("NULL vdev"); + return NULL; + } + + wlan_vdev_obj_lock(vdev); + vdev_obj = (struct tdls_vdev_priv_obj *) wlan_objmgr_vdev_get_comp_private_obj(vdev, WLAN_UMAC_COMP_TDLS); + wlan_vdev_obj_unlock(vdev); + + return vdev_obj; } /** * tdls_set_link_status - tdls set link status * @vdev: vdev object * @mac: mac address of tdls peer - * @link_status: tdls link status + * @link_state: tdls link state * @link_reason: reason */ void tdls_set_link_status(struct tdls_vdev_priv_obj *vdev, const uint8_t *mac, - enum tdls_link_status link_status, - enum tdls_link_reason link_reason); + enum tdls_link_state link_state, + enum tdls_link_state_reason link_reason); /** * tdls_psoc_obj_create_notification() - tdls psoc create notification handler * @psoc: psoc object diff --git a/umac/tdls/core/src/wlan_tdls_peer.c b/umac/tdls/core/src/wlan_tdls_peer.c new file mode 100644 index 000000000000..a89736fb95e0 --- /dev/null +++ b/umac/tdls/core/src/wlan_tdls_peer.c @@ -0,0 +1,836 @@ +/* + * Copyright (c) 2017 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_peer.c + * + * TDLS peer basic operations + */ +#include "wlan_tdls_main.h" +#include "wlan_tdls_peer.h" +#include +#include +#include + +static uint8_t calculate_hash_key(const uint8_t *macaddr) +{ + uint8_t i, key; + + for (i = 0, key = 0; i < 6; i++) + key ^= macaddr[i]; + + return key % WLAN_TDLS_PEER_LIST_SIZE; +} + +struct tdls_peer *tdls_find_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr) +{ + uint8_t key; + QDF_STATUS status; + struct tdls_peer *peer; + qdf_list_t *head; + qdf_list_node_t *p_node; + + key = calculate_hash_key(macaddr); + head = &vdev_obj->peer_list[key]; + + status = qdf_list_peek_front(head, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + peer = qdf_container_of(p_node, struct tdls_peer, node); + if (WLAN_ADDR_EQ(&peer->peer_mac, macaddr) + == QDF_STATUS_SUCCESS) { + return peer; + } + status = qdf_list_peek_next(head, p_node, &p_node); + } + + tdls_debug("no tdls peer " QDF_MAC_ADDRESS_STR, + QDF_MAC_ADDR_ARRAY(macaddr)); + return NULL; +} + +/** + * tdls_find_peer_handler() - helper function for tdls_find_all_peer + * @psoc: soc object + * @obj: vdev object + * @arg: used to keep search peer parameters + * + * Return: None. + */ +static void +tdls_find_peer_handler(struct wlan_objmgr_psoc *psoc, void *obj, void *arg) +{ + struct wlan_objmgr_vdev *vdev = obj; + struct tdls_search_peer_param *tdls_param = arg; + struct tdls_vdev_priv_obj *vdev_obj; + + if (tdls_param->peer) + return; + + if (!vdev) { + tdls_err("invalid vdev"); + return; + } + + if (wlan_vdev_mlme_get_opmode(vdev) != QDF_STA_MODE && + wlan_vdev_mlme_get_opmode(vdev) != QDF_P2P_CLIENT_MODE) + return; + + vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_TDLS); + if (!vdev_obj) + return; + + tdls_param->peer = tdls_find_peer(vdev_obj, tdls_param->macaddr); +} + +struct tdls_peer * +tdls_find_all_peer(struct tdls_soc_priv_obj *soc_obj, const uint8_t *macaddr) +{ + struct tdls_search_peer_param tdls_search_param; + struct wlan_objmgr_psoc *psoc; + + if (!soc_obj) { + tdls_err("tdls soc object is NULL"); + return NULL; + } + + psoc = soc_obj->soc; + if (!psoc) { + tdls_err("psoc is NULL"); + return NULL; + } + tdls_search_param.macaddr = macaddr; + tdls_search_param.peer = NULL; + + wlan_objmgr_iterate_obj_list(psoc, WLAN_VDEV_OP, + tdls_find_peer_handler, + &tdls_search_param, 0, WLAN_TDLS_NB_ID); + + return tdls_search_param.peer; +} + +static uint8_t tdls_find_opclass(struct wlan_objmgr_psoc *psoc, uint8_t channel, + uint8_t bw_offset) +{ + char country[REG_ALPHA2_LEN + 1]; + QDF_STATUS status; + + if (!psoc) { + tdls_err("psoc is NULL"); + return 0; + } + + status = wlan_reg_read_default_country(psoc, country); + if (QDF_IS_STATUS_ERROR(status)) + return 0; + + return wlan_reg_dmn_get_opclass_from_channel(country, channel, + bw_offset); +} + +/** + * tdls_add_peer() - add TDLS peer in TDLS vdev object + * @vdev_obj: TDLS vdev object + * @macaddr: MAC address of peer + * + * Allocate memory for the new peer, and add it to hash table. + * + * Return: new added TDLS peer, NULL if failed. + */ +static struct tdls_peer *tdls_add_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr) +{ + struct tdls_peer *peer; + struct tdls_soc_priv_obj *soc_obj; + uint8_t key = 0; + qdf_list_t *head; + + peer = qdf_mem_malloc(sizeof(*peer)); + if (!peer) { + tdls_err("add tdls peer malloc memory failed!"); + return NULL; + } + + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev); + if (!soc_obj) { + tdls_err("NULL tdls soc object"); + return NULL; + } + + key = calculate_hash_key(macaddr); + head = &vdev_obj->peer_list[key]; + + qdf_mem_copy(&peer->peer_mac, macaddr, sizeof(peer->peer_mac)); + peer->vdev_priv = vdev_obj; + + peer->pref_off_chan_num = + soc_obj->tdls_configs.tdls_pre_off_chan_num; + peer->op_class_for_pref_off_chan = + tdls_find_opclass(soc_obj->soc, + peer->pref_off_chan_num, + soc_obj->tdls_configs.tdls_pre_off_chan_bw); + + qdf_list_insert_back(head, &peer->node); + + tdls_debug("add tdls peer: " QDF_MAC_ADDRESS_STR, + QDF_MAC_ADDR_ARRAY(macaddr)); + return peer; +} + +struct tdls_peer *tdls_get_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr) +{ + struct tdls_peer *peer; + + peer = tdls_find_peer(vdev_obj, macaddr); + if (!peer) + peer = tdls_add_peer(vdev_obj, macaddr); + + return peer; +} + +static struct tdls_peer * +tdls_find_progress_peer_in_list(qdf_list_t *head, + const uint8_t *macaddr, uint8_t skip_self) +{ + QDF_STATUS status; + struct tdls_peer *peer; + qdf_list_node_t *p_node; + + status = qdf_list_peek_front(head, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + peer = qdf_container_of(p_node, struct tdls_peer, node); + if (skip_self && macaddr && + WLAN_ADDR_EQ(&peer->peer_mac, macaddr) + == QDF_STATUS_SUCCESS) { + status = qdf_list_peek_next(head, p_node, &p_node); + continue; + } else if (TDLS_LINK_CONNECTING == peer->link_status) { + tdls_debug(QDF_MAC_ADDRESS_STR " TDLS_LINK_CONNECTING", + QDF_MAC_ADDR_ARRAY(peer->peer_mac.bytes)); + return peer; + } + status = qdf_list_peek_next(head, p_node, &p_node); + } + + return NULL; +} + +/** + * tdls_find_progress_peer() - find the peer with ongoing TDLS progress + * on present vdev + * @vdev_obj: TDLS vdev object + * @macaddr: MAC address of peer, if NULL check for all the peer list + * @skip_self: If true, skip this macaddr. Otherwise, check all the peer list. + * if macaddr is NULL, this argument is ignored, and check for all + * the peer list. + * + * Return: Pointer to tdls_peer if TDLS is ongoing. Otherwise return NULL. + */ +static struct tdls_peer * +tdls_find_progress_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr, uint8_t skip_self) +{ + uint8_t i; + struct tdls_peer *peer; + qdf_list_t *head; + + if (!vdev_obj) { + tdls_err("invalid tdls vdev object"); + return NULL; + } + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &vdev_obj->peer_list[i]; + + peer = tdls_find_progress_peer_in_list(head, macaddr, + skip_self); + if (peer) + return peer; + } + + return NULL; +} + +/** + * tdls_find_progress_peer_handler() - helper function for tdls_is_progress + * @psoc: soc object + * @obj: vdev object + * @arg: used to keep search peer parameters + * + * Return: None. + */ +static void +tdls_find_progress_peer_handler(struct wlan_objmgr_psoc *psoc, + void *obj, void *arg) +{ + struct wlan_objmgr_vdev *vdev = obj; + struct tdls_search_progress_param *tdls_progress = arg; + struct tdls_vdev_priv_obj *vdev_obj; + + if (tdls_progress->peer) + return; + + if (!vdev) { + tdls_err("invalid vdev"); + return; + } + + if (wlan_vdev_mlme_get_opmode(vdev) != QDF_STA_MODE && + wlan_vdev_mlme_get_opmode(vdev) != QDF_P2P_CLIENT_MODE) + return; + + vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_TDLS); + + tdls_progress->peer = tdls_find_progress_peer(vdev_obj, + tdls_progress->macaddr, + tdls_progress->skip_self); +} + +struct tdls_peer *tdls_is_progress(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr, uint8_t skip_self) +{ + struct tdls_search_progress_param tdls_progress; + struct wlan_objmgr_psoc *psoc; + + if (!vdev_obj) { + tdls_err("invalid tdls vdev object"); + return NULL; + } + + wlan_vdev_obj_lock(vdev_obj->vdev); + psoc = wlan_vdev_get_psoc(vdev_obj->vdev); + wlan_vdev_obj_unlock(vdev_obj->vdev); + if (!psoc) { + tdls_err("invalid psoc"); + return NULL; + } + tdls_progress.macaddr = macaddr; + tdls_progress.skip_self = skip_self; + tdls_progress.peer = NULL; + + wlan_objmgr_iterate_obj_list(psoc, WLAN_VDEV_OP, + tdls_find_progress_peer_handler, + &tdls_progress, 0, WLAN_TDLS_NB_ID); + + return tdls_progress.peer; +} + +struct tdls_peer * +tdls_find_first_connected_peer(struct tdls_vdev_priv_obj *vdev_obj) +{ + uint16_t i; + struct tdls_peer *peer; + qdf_list_t *head; + qdf_list_node_t *p_node; + QDF_STATUS status; + + if (!vdev_obj) { + tdls_err("invalid tdls vdev object"); + return NULL; + } + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &vdev_obj->peer_list[i]; + + status = qdf_list_peek_front(head, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + peer = qdf_container_of(p_node, struct tdls_peer, node); + + if (peer && TDLS_LINK_CONNECTED == peer->link_status) { + tdls_debug(QDF_MAC_ADDRESS_STR + " TDLS_LINK_CONNECTED", + QDF_MAC_ADDR_ARRAY( + peer->peer_mac.bytes)); + return peer; + } + status = qdf_list_peek_next(head, p_node, &p_node); + } + } + + return NULL; +} + +/** + * tdls_determine_channel_opclass() - determine channel and opclass + * @soc_obj: TDLS soc object + * @vdev_obj: TDLS vdev object + * @peer: TDLS peer + * @channel: pointer to channel + * @opclass: pinter to opclass + * + * Function determines the channel and operating class + * + * Return: None. + */ +static void tdls_determine_channel_opclass(struct tdls_soc_priv_obj *soc_obj, + struct tdls_vdev_priv_obj *vdev_obj, + struct tdls_peer *peer, + uint32_t *channel, uint32_t *opclass) +{ + uint32_t vdev_id; + enum tQDF_ADAPTER_MODE opmode; + /* + * If tdls offchannel is not enabled then we provide base channel + * and in that case pass opclass as 0 since opclass is mainly needed + * for offchannel cases. + */ + if (!(TDLS_IS_OFF_CHANNEL_ENABLED( + soc_obj->tdls_configs.tdls_feature_flags)) || + soc_obj->tdls_fw_off_chan_mode != ENABLE_CHANSWITCH) { + wlan_vdev_obj_lock(vdev_obj->vdev); + vdev_id = wlan_vdev_get_id(vdev_obj->vdev); + opmode = wlan_vdev_mlme_get_opmode(vdev_obj->vdev); + wlan_vdev_obj_unlock(vdev_obj->vdev); + + *channel = policy_mgr_get_channel(soc_obj->soc, + opmode, &vdev_id); + *opclass = 0; + } else { + *channel = peer->pref_off_chan_num; + *opclass = peer->op_class_for_pref_off_chan; + } + tdls_debug("channel:%d opclass:%d", *channel, *opclass); +} + +/** + * tdls_get_wifi_hal_state() - get TDLS wifi hal state on current peer + * @peer: TDLS peer + * @state: output parameter to store the TDLS wifi hal state + * @reason: output parameter to store the reason of the current peer + * + * Return: None. + */ +static void tdls_get_wifi_hal_state(struct tdls_peer *peer, uint32_t *state, + int32_t *reason) +{ + struct wlan_objmgr_vdev *vdev; + struct tdls_soc_priv_obj *soc_obj; + + vdev = peer->vdev_priv->vdev; + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (!soc_obj) { + tdls_err("can't get tdls object"); + return; + } + + *reason = peer->reason; + + switch (peer->link_status) { + case TDLS_LINK_IDLE: + case TDLS_LINK_DISCOVERED: + case TDLS_LINK_DISCOVERING: + case TDLS_LINK_CONNECTING: + *state = QCA_WIFI_HAL_TDLS_S_ENABLED; + break; + case TDLS_LINK_CONNECTED: + if ((TDLS_IS_OFF_CHANNEL_ENABLED( + soc_obj->tdls_configs.tdls_feature_flags)) && + (soc_obj->tdls_fw_off_chan_mode == ENABLE_CHANSWITCH)) + *state = QCA_WIFI_HAL_TDLS_S_ESTABLISHED_OFF_CHANNEL; + else + *state = QCA_WIFI_HAL_TDLS_S_ENABLED; + break; + case TDLS_LINK_TEARING: + *state = QCA_WIFI_HAL_TDLS_S_DROPPED; + break; + } +} + +/** + * tdls_extract_peer_state_param() - extract peer update params from TDLS peer + * @peer_param: output peer update params + * @peer: TDLS peer + * + * This is used when enable TDLS link + * + * Return: None. + */ +void tdls_extract_peer_state_param(struct tdls_peer_update_state *peer_param, + struct tdls_peer *peer) +{ + uint16_t i, num; + struct tdls_vdev_priv_obj *vdev_obj; + struct tdls_soc_priv_obj *soc_obj; + enum channel_state ch_state; + struct wlan_objmgr_pdev *pdev; + uint8_t chan_id; + + vdev_obj = peer->vdev_priv; + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev); + pdev = wlan_vdev_get_pdev(vdev_obj->vdev); + if (!soc_obj || !pdev) { + tdls_err("soc_obj: %p, pdev: %p", soc_obj, pdev); + return; + } + + qdf_mem_zero(peer_param, sizeof(*peer_param)); + wlan_vdev_obj_lock(vdev_obj->vdev); + peer_param->vdev_id = wlan_vdev_get_id(vdev_obj->vdev); + wlan_vdev_obj_unlock(vdev_obj->vdev); + + qdf_mem_copy(peer_param->peer_macaddr, + peer->peer_mac.bytes, QDF_MAC_ADDR_SIZE); + peer_param->peer_state = TDLS_PEER_STATE_CONNCTED; + peer_param->peer_cap.is_peer_responder = peer->is_responder; + peer_param->peer_cap.peer_uapsd_queue = peer->uapsd_queues; + peer_param->peer_cap.peer_max_sp = peer->max_sp; + peer_param->peer_cap.peer_buff_sta_support = peer->buf_sta_capable; + peer_param->peer_cap.peer_off_chan_support = + peer->off_channel_capable; + peer_param->peer_cap.peer_curr_operclass = 0; + peer_param->peer_cap.self_curr_operclass = 0; + peer_param->peer_cap.peer_chanlen = peer->supported_channels_len; + peer_param->peer_cap.pref_off_channum = peer->pref_off_chan_num; + peer_param->peer_cap.pref_off_chan_bandwidth = + soc_obj->tdls_configs.tdls_pre_off_chan_bw; + peer_param->peer_cap.opclass_for_prefoffchan = + peer->op_class_for_pref_off_chan; + + if (wlan_reg_is_dfs_ch(pdev, peer_param->peer_cap.pref_off_channum)) { + tdls_err("Resetting TDLS off-channel from %d to %d", + peer_param->peer_cap.pref_off_channum, + WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_DEF); + peer_param->peer_cap.pref_off_channum = + WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_DEF; + } + + num = 0; + for (i = 0; i < peer->supported_channels_len; i++) { + chan_id = peer->supported_channels[i]; + ch_state = wlan_reg_get_channel_state(pdev, chan_id); + + if (CHANNEL_STATE_INVALID != ch_state && + CHANNEL_STATE_DFS != ch_state && + !wlan_is_dsrc_channel(wlan_is_dsrc_channel(chan_id))) { + peer_param->peer_cap.peer_chan[num].chan_id = chan_id; + peer_param->peer_cap.peer_chan[num].pwr = + wlan_reg_get_channel_reg_power(pdev, chan_id); + peer_param->peer_cap.peer_chan[num].dfs_set = false; + num++; + } + } + + peer_param->peer_cap.peer_oper_classlen = + peer->supported_oper_classes_len; + for (i = 0; i < peer->supported_oper_classes_len; i++) + peer_param->peer_cap.peer_oper_class[i] = + peer->supported_oper_classes[i]; +} + +/** + * tdls_set_link_status() - set link statue for TDLS peer + * @vdev_obj: TDLS vdev object + * @mac: MAC address of current TDLS peer + * @link_status: link status + * @link_reason: reason with link status + * + * Return: None. + */ +void tdls_set_link_status(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *mac, + enum tdls_link_state link_status, + enum tdls_link_state_reason link_reason) +{ + uint32_t state = 0; + int32_t res = 0; + uint32_t op_class = 0; + uint32_t channel = 0; + struct tdls_peer *peer; + struct tdls_soc_priv_obj *soc_obj; + + peer = tdls_find_peer(vdev_obj, mac); + if (!peer) { + tdls_err("peer is NULL, can't set link status %d, reason %d", + link_status, link_reason); + return; + } + + peer->link_status = link_status; + + if (link_status >= TDLS_LINK_DISCOVERED) + peer->discovery_attempt = 0; + + if (peer->is_forced_peer && peer->state_change_notification) { + peer->reason = link_reason; + + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev); + if (!soc_obj) { + tdls_err("NULL psoc object"); + return; + } + + tdls_determine_channel_opclass(soc_obj, vdev_obj, + peer, &channel, &op_class); + tdls_get_wifi_hal_state(peer, &state, &res); + peer->state_change_notification(mac, op_class, channel, + state, res, soc_obj->soc); + } +} + +void tdls_set_peer_link_status(struct tdls_peer *peer, + enum tdls_link_state link_status, + enum tdls_link_state_reason link_reason) +{ + uint32_t state = 0; + int32_t res = 0; + uint32_t op_class = 0; + uint32_t channel = 0; + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + + peer->link_status = link_status; + + if (link_status >= TDLS_LINK_DISCOVERED) + peer->discovery_attempt = 0; + + if (peer->is_forced_peer && peer->state_change_notification) { + peer->reason = link_reason; + + vdev_obj = peer->vdev_priv; + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev); + if (!soc_obj) { + tdls_err("NULL psoc object"); + return; + } + + tdls_determine_channel_opclass(soc_obj, vdev_obj, + peer, &channel, &op_class); + tdls_get_wifi_hal_state(peer, &state, &res); + peer->state_change_notification(peer->peer_mac.bytes, + op_class, channel, state, + res, soc_obj->soc); + } +} + +void tdls_set_peer_caps(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr, + struct tdls_update_peer_params *req_info) +{ + uint8_t is_buffer_sta, is_off_channel_supported, is_qos_wmm_sta; + struct tdls_soc_priv_obj *soc_obj; + struct tdls_peer *curr_peer; + uint32_t feature; + + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev); + if (!soc_obj) { + tdls_err("NULL psoc object"); + return; + } + + curr_peer = tdls_find_peer(vdev_obj, macaddr); + if (!curr_peer) { + tdls_err("NULL tdls peer"); + return; + } + + feature = soc_obj->tdls_configs.tdls_feature_flags; + if ((1 << 4) & req_info->extn_capability[3]) + is_buffer_sta = 1; + + if ((1 << 6) & req_info->extn_capability[3]) + is_off_channel_supported = 1; + + if (TDLS_IS_WMM_ENABLED(feature) && req_info->is_qos_wmm_sta) + is_qos_wmm_sta = true; + + curr_peer->uapsd_queues = req_info->uapsd_queues; + curr_peer->max_sp = req_info->max_sp; + curr_peer->buf_sta_capable = is_buffer_sta; + curr_peer->off_channel_capable = is_off_channel_supported; + + qdf_mem_copy(curr_peer->supported_channels, + req_info->supported_channels, + req_info->supported_channels_len); + + curr_peer->supported_channels_len = req_info->supported_channels_len; + + qdf_mem_copy(curr_peer->supported_oper_classes, + req_info->supported_oper_classes, + req_info->supported_oper_classes_len); + + curr_peer->supported_oper_classes_len = + req_info->supported_oper_classes_len; + + curr_peer->qos = is_qos_wmm_sta; +} + +QDF_STATUS tdls_set_sta_id(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr, uint8_t sta_id) +{ + struct tdls_peer *peer; + + peer = tdls_find_peer(vdev_obj, macaddr); + if (!peer) { + tdls_err("peer is NULL"); + return QDF_STATUS_E_FAILURE; + } + + peer->sta_id = sta_id; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_set_signature(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr, uint8_t signature) +{ + struct tdls_peer *peer; + + peer = tdls_find_peer(vdev_obj, macaddr); + if (!peer) { + tdls_err("peer is NULL"); + return QDF_STATUS_E_FAILURE; + } + peer->signature = signature; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_set_force_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr, bool forcepeer) +{ + struct tdls_peer *peer; + + peer = tdls_find_peer(vdev_obj, macaddr); + if (!peer) { + tdls_err("peer is NULL"); + return QDF_STATUS_E_FAILURE; + } + peer->is_forced_peer = forcepeer; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_set_callback(struct tdls_peer *peer, + tdls_state_change_callback callback) +{ + if (!peer) { + tdls_err("peer is NULL"); + return QDF_STATUS_E_FAILURE; + } + peer->state_change_notification = callback; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_set_extctrl_param(struct tdls_peer *peer, uint32_t chan, + uint32_t max_latency, uint32_t op_class, + uint32_t min_bandwidth) +{ + if (!peer) { + tdls_err("peer is NULL"); + return QDF_STATUS_E_FAILURE; + } + peer->op_class_for_pref_off_chan = (uint8_t)op_class; + peer->pref_off_chan_num = (uint8_t)chan; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_reset_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr) +{ + struct tdls_soc_priv_obj *soc_obj; + struct tdls_peer *curr_peer; + struct tdls_user_config *config; + + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev); + if (!soc_obj) { + tdls_err("NULL psoc object"); + return QDF_STATUS_E_FAILURE; + } + + curr_peer = tdls_find_peer(vdev_obj, macaddr); + if (!curr_peer) { + tdls_err("NULL tdls peer"); + return QDF_STATUS_E_FAILURE; + } + + if (!curr_peer->is_forced_peer) { + config = &soc_obj->tdls_configs; + curr_peer->pref_off_chan_num = config->tdls_pre_off_chan_num; + curr_peer->op_class_for_pref_off_chan = + tdls_find_opclass(soc_obj->soc, + curr_peer->pref_off_chan_num, + config->tdls_pre_off_chan_bw); + } + + tdls_set_peer_link_status(curr_peer, TDLS_LINK_IDLE, + TDLS_LINK_UNSPECIFIED); + curr_peer->sta_id = 0; + + return QDF_STATUS_SUCCESS; +} + +void tdls_peer_idle_timers_destroy(struct tdls_vdev_priv_obj *vdev_obj) +{ + uint16_t i; + struct tdls_peer *peer; + qdf_list_t *head; + qdf_list_node_t *p_node; + QDF_STATUS status; + + if (!vdev_obj) { + tdls_err("NULL tdls vdev object"); + return; + } + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &vdev_obj->peer_list[i]; + + status = qdf_list_peek_front(head, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + peer = qdf_container_of(p_node, struct tdls_peer, node); + if (peer && peer->is_peer_idle_timer_initialised) { + tdls_debug(QDF_MAC_ADDRESS_STR + ": destroy idle timer ", + QDF_MAC_ADDR_ARRAY( + peer->peer_mac.bytes)); + qdf_mc_timer_stop(&peer->peer_idle_timer); + qdf_mc_timer_destroy(&peer->peer_idle_timer); + } + status = qdf_list_peek_next(head, p_node, &p_node); + } + } +} + +void tdls_free_peer_list(struct tdls_vdev_priv_obj *vdev_obj) +{ + uint16_t i; + struct tdls_peer *peer; + qdf_list_t *head; + qdf_list_node_t *p_node; + + if (!vdev_obj) { + tdls_err("NULL tdls vdev object"); + return; + } + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &vdev_obj->peer_list[i]; + + while (QDF_IS_STATUS_SUCCESS( + qdf_list_remove_front(head, &p_node))) { + peer = qdf_container_of(p_node, struct tdls_peer, node); + qdf_mem_free(peer); + } + qdf_list_destroy(head); + } +} diff --git a/umac/tdls/core/src/wlan_tdls_peer.h b/umac/tdls/core/src/wlan_tdls_peer.h new file mode 100644 index 000000000000..511b4d4ed010 --- /dev/null +++ b/umac/tdls/core/src/wlan_tdls_peer.h @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2017 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_peer.h + * + * TDLS peer function declaration + */ + +#if !defined(_WLAN_TDLS_PEER_H_) +#define _WLAN_TDLS_PEER_H_ + +/** + * struct tdls_search_peer_param - used to search TDLS peer + * @macaddr: MAC address of peer + * @peer: pointer to the found peer + */ +struct tdls_search_peer_param { + const uint8_t *macaddr; + struct tdls_peer *peer; +}; + +/** + * struct tdls_progress_param - used to search progress TDLS peer + * @skip_self: skip self peer + * @macaddr: MAC address of peer + * @peer: pointer to the found peer + */ +struct tdls_search_progress_param { + uint8_t skip_self; + const uint8_t *macaddr; + struct tdls_peer *peer; +}; + +/** + * tdls_get_peer() - find or add an TDLS peer in TDLS vdev object + * @vdev_obj: TDLS vdev object + * @macaddr: MAC address of peer + * + * Search the TDLS peer in the hash table and create a new one if not found. + * + * Return: Pointer to tdls_peer, NULL if failed. + */ +struct tdls_peer *tdls_get_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr); + +/** + * tdls_find_peer() - find TDLS peer in TDLS vdev object + * @vdev_obj: TDLS vdev object + * @macaddr: MAC address of peer + * + * This is in scheduler thread context, no lock required. + * + * Return: If peer is found, then it returns pointer to tdls_peer; + * otherwise, it returns NULL. + */ +struct tdls_peer *tdls_find_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr); + +/** + * tdls_find_all_peer() - find peer matching the input MACaddr in soc range + * @soc_obj: TDLS soc object + * @macaddr: MAC address of TDLS peer + * + * This is in scheduler thread context, no lock required. + * + * Return: TDLS peer if a matching is detected; NULL otherwise + */ +struct tdls_peer * +tdls_find_all_peer(struct tdls_soc_priv_obj *soc_obj, const uint8_t *macaddr); + +/** + * tdls_find_first_connected_peer() - find the 1st connected tdls peer from vdev + * @vdev_obj: tdls vdev object + * + * This function searches for the 1st connected TDLS peer + * + * Return: The 1st connected TDLS peer if found; NULL otherwise + */ +struct tdls_peer * +tdls_find_first_connected_peer(struct tdls_vdev_priv_obj *vdev_obj); + +/** + * tdls_is_progress() - find the peer with ongoing TDLS progress on present psoc + * @vdev_obj: TDLS vdev object + * @macaddr: MAC address of the peer + * @skip_self: if 1, skip checking self. If 0, search include self + * + * This is used in scheduler thread context, no lock required. + * + * Return: TDLS peer if found; NULL otherwise + */ +struct tdls_peer *tdls_is_progress(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr, uint8_t skip_self); + +/** + * tdls_extract_peer_state_param() - extract peer update params from TDL peer + * @peer_param: output peer update params + * @peer: TDLS peer + * + * This is used when enable TDLS link + * + * Return: None. + */ +void tdls_extract_peer_state_param(struct tdls_peer_update_state *peer_param, + struct tdls_peer *peer); + +/** + * tdls_set_link_status() - set link statue for TDLS peer + * @peer: TDLS peer + * @link_state: link state + * @link_reason: reason with link status + * + * This is in scheduler thread context, no lock required. + * + * Return: None. + */ +void tdls_set_peer_link_status(struct tdls_peer *peer, + enum tdls_link_state link_state, + enum tdls_link_state_reason link_reason); + +/** + * tdls_set_peer_caps() - set capability for TDLS peer + * @vdev_obj: TDLS vdev object + * @macaddr: MAC address for the TDLS peer + * @req_info: parameters to update peer capability + * + * This is in scheduler thread context, no lock required. + * + * Return: None. + */ +void tdls_set_peer_caps(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr, + struct tdls_update_peer_params *req_info); + +/** + * tdls_set_sta_id() - set station ID on a TDLS peer + * @vdev_obj: TDLS vdev object + * @macaddr: MAC address of the TDLS peer + * @sta_id: station ID + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +QDF_STATUS tdls_set_sta_id(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr, uint8_t sta_id); + +/** + * tdls_set_signature() - set TDLS peer's signature + * @vdev_obj: TDLS vdev object + * @macaddr: MAC address of the TDLS peer + * @signature: signature value + * + * This is used in scheduler thread context, no lock required. + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +QDF_STATUS tdls_set_signature(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr, uint8_t signature); + +/** + * tdls_set_force_peer() - set/clear is_forced_peer flag on peer + * @vdev_obj: TDLS vdev object + * @macaddr: MAC address of TDLS peer + * @forcepeer: value used to set is_forced_peer flag + * + * This is used in scheduler thread context, no lock required. + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +QDF_STATUS tdls_set_force_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *macaddr, bool forcepeer); + +/** + * tdls_set_callback() - set state change callback on current TDLS peer + * @peer: TDLS peer + * @callback: state change callback + * + * This is used in scheduler thread context, no lock required. + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +QDF_STATUS tdls_set_callback(struct tdls_peer *peer, + tdls_state_change_callback callback); + +/** + * tdls_set_extctrl_param() - set external control parameter on TDLS peer + * @peer: TDLS peer + * @chan: channel + * @max_latency: maximum latency + * @op_class: operation class + * @min_bandwidth: minimal bandwidth + * + * This is used in scheduler thread context, no lock required. + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +QDF_STATUS tdls_set_extctrl_param(struct tdls_peer *peer, uint32_t chan, + uint32_t max_latency, uint32_t op_class, + uint32_t min_bandwidth); + +/** + * tdls_reset_peer() - reset TDLS peer identified by MAC address + * @vdev_obj: TDLS vdev object + * @mac: MAC address of the peer + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +QDF_STATUS tdls_reset_peer(struct tdls_vdev_priv_obj *vdev_obj, + const uint8_t *mac); + +/** + * tdls_peer_idle_timers_destroy() - destroy peer idle timers + * @vdev_obj: TDLS vdev object + * + * Loop through the idle peer list and destroy their timers + * + * Return: None + */ +void tdls_peer_idle_timers_destroy(struct tdls_vdev_priv_obj *vdev_obj); + +/** + * tdls_free_peer_list() - free TDLS peer list + * @vdev_obj: TDLS vdev object + * + * Free all the tdls peers + * + * Return: None + */ +void tdls_free_peer_list(struct tdls_vdev_priv_obj *vdev_obj); +#endif diff --git a/umac/tdls/dispatcher/inc/wlan_tdls_public_structs.h b/umac/tdls/dispatcher/inc/wlan_tdls_public_structs.h index d9b5d8da985d..2f76979be4fb 100644 --- a/umac/tdls/dispatcher/inc/wlan_tdls_public_structs.h +++ b/umac/tdls/dispatcher/inc/wlan_tdls_public_structs.h @@ -30,15 +30,36 @@ #include #include -#define WLAN_TDLS_STA_MAX_NUM 8 -#define WLAN_TDLS_PEER_LIST_SIZE 256 -#define WLAN_TDLS_CT_TABLE_SIZE 8 -#define WLAN_TDLS_PEER_SUB_LIST_SIZE 10 -#define WLAN_MAC_MAX_EXTN_CAP 8 -#define WLAN_MAC_MAX_SUPP_CHANNELS 100 -#define WLAN_MAX_SUPP_OPER_CLASSES 32 -#define WLAN_MAC_MAX_SUPP_RATES 32 -#define ENABLE_CHANSWITCH 1 +#define WLAN_TDLS_STA_MAX_NUM 8 +#define WLAN_TDLS_STA_P_UAPSD_OFFCHAN_MAX_NUM 1 +#define WLAN_TDLS_PEER_LIST_SIZE 16 +#define WLAN_TDLS_CT_TABLE_SIZE 8 +#define WLAN_TDLS_PEER_SUB_LIST_SIZE 10 +#define WLAN_MAC_MAX_EXTN_CAP 8 +#define WLAN_MAC_MAX_SUPP_CHANNELS 100 +#define WLAN_MAX_SUPP_OPER_CLASSES 32 +#define WLAN_MAC_MAX_SUPP_RATES 32 +#define WLAN_CHANNEL_14 14 +#define ENABLE_CHANSWITCH 1 +#define DISABLE_CHANSWITCH 2 +#define WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_MIN 1 +#define WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_MAX 165 +#define WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_DEF 36 + +/** Maximum time(ms) to wait for tdls add sta to complete **/ +#define WAIT_TIME_TDLS_ADD_STA 1500 + +/** Maximum time(ms) to wait for tdls del sta to complete **/ +#define WAIT_TIME_TDLS_DEL_STA 1500 + +/** Maximum time(ms) to wait for Link Establish Req to complete **/ +#define WAIT_TIME_TDLS_LINK_ESTABLISH_REQ 1500 + +/** Maximum time(ms) to wait for tdls mgmt to complete **/ +#define WAIT_TIME_TDLS_MGMT 11000 + +#define TDLS_TEARDOWN_PEER_UNREACHABLE 25 +#define TDLS_TEARDOWN_PEER_UNSPEC_REASON 26 #define TDLS_STA_INDEX_CHECK(sta_id) \ (((sta_id) >= 1) && ((sta_id) < 0xFF)) @@ -65,7 +86,23 @@ enum tdls_peer_capab { }; /** - * enum tdls_link_status - tdls link status + * enum tdls_peer_state - tdls peer state + * @TDLS_PEER_STATE_PEERING: tdls connection in progress + * @TDLS_PEER_STATE_CONNCTED: tdls peer is connected + * @TDLS_PEER_STATE_TEARDOWN: tdls peer is tear down + * @TDLS_PEER_ADD_MAC_ADDR: add peer mac into connection table + * @TDLS_PEER_REMOVE_MAC_ADDR: remove peer mac from connection table + */ +enum tdls_peer_state { + TDLS_PEER_STATE_PEERING, + TDLS_PEER_STATE_CONNCTED, + TDLS_PEER_STATE_TEARDOWN, + TDLS_PEER_ADD_MAC_ADDR, + TDLS_PEER_REMOVE_MAC_ADDR +}; + +/** + * enum tdls_link_state - tdls link state * @TDLS_LINK_IDLE: tdls link idle * @TDLS_LINK_DISCOVERING: tdls link discovering * @TDLS_LINK_DISCOVERED: tdls link discovered @@ -73,7 +110,7 @@ enum tdls_peer_capab { * @TDLS_LINK_CONNECTED: tdls link connected * @TDLS_LINK_TEARING: tdls link tearing */ -enum tdls_link_status { +enum tdls_link_state { TDLS_LINK_IDLE = 0, TDLS_LINK_DISCOVERING, TDLS_LINK_DISCOVERED, @@ -83,7 +120,7 @@ enum tdls_link_status { }; /** - * enum tdls_link_reason - tdls link reason + * enum tdls_link_state_reason - tdls link reason * @TDLS_LINK_SUCCESS: Success * @TDLS_LINK_UNSPECIFIED: Unspecified reason * @TDLS_LINK_NOT_SUPPORTED: Remote side doesn't support TDLS @@ -91,7 +128,7 @@ enum tdls_link_status { * @TDLS_LINK_NOT_BENEFICIAL: Going to AP is better than direct * @TDLS_LINK_DROPPED_BY_REMOTE: Remote side doesn't want it anymore */ -enum tdls_link_reason { +enum tdls_link_state_reason { TDLS_LINK_SUCCESS, TDLS_LINK_UNSPECIFIED = -1, TDLS_LINK_NOT_SUPPORTED = -2, @@ -101,7 +138,7 @@ enum tdls_link_reason { }; /** - * enum tdls_support_mode - TDLS support mode + * enum tdls_feature_mode - TDLS support mode * @TDLS_SUPPORT_DISABLED: Disabled in ini or FW * @TDLS_SUPPORT_SUSPENDED: TDLS supported by ini and FW, but disabled * temporarily due to off-channel operations or due to other reasons @@ -109,7 +146,7 @@ enum tdls_link_reason { * @TDLS_SUPPORT_IMP_MODE: Implicit mode * @TDLS_SUPPORT_EXT_CONTROL: External control mode */ -enum tdls_support_mode { +enum tdls_feature_mode { TDLS_SUPPORT_DISABLED = 0, TDLS_SUPPORT_SUSPENDED, TDLS_SUPPORT_EXP_TRIG_ONLY, @@ -124,6 +161,9 @@ enum tdls_support_mode { * @TDLS_EVENT_RX_MGMT: rx discovery response frame * @TDLS_EVENT_ADD_PEER: add peer or update peer * @TDLS_EVENT_DEL_PEER: delete peer + * @TDLS_EVENT_DISCOVERY_REQ: dicovery request + * @TDLS_EVENT_TEARDOWN_REQ: teardown request + * @TDLS_EVENT_SETUP_REQ: setup request */ enum tdls_event_type { TDLS_EVENT_VDEV_STATE_CHANGE = 0, @@ -131,11 +171,13 @@ enum tdls_event_type { TDLS_EVENT_RX_MGMT, TDLS_EVENT_ADD_PEER, TDLS_EVENT_DEL_PEER, + TDLS_EVENT_DISCOVERY_REQ, + TDLS_EVENT_TEARDOWN_REQ, + TDLS_EVENT_SETUP_REQ, }; /** * enum tdls_state_t - tdls state - * * @QCA_WIFI_HAL_TDLS_DISABLED: TDLS is not enabled, or is disabled now * @QCA_WIFI_HAL_TDLS_ENABLED: TDLS is enabled, but not yet tried * @QCA_WIFI_HAL_TDLS_ESTABLISHED: Direct link is established @@ -152,12 +194,25 @@ enum tdls_state_t { QCA_WIFI_HAL_TDLS_S_FAILED, }; +/** + * enum tdls_off_chan_mode - mode for WMI_TDLS_SET_OFFCHAN_MODE_CMDID + * @TDLS_ENABLE_OFFCHANNEL: enable off channel + * @TDLS_DISABLE_OFFCHANNEL: disable off channel + */ +enum tdls_off_chan_mode { + TDLS_ENABLE_OFFCHANNEL, + TDLS_DISABLE_OFFCHANNEL +}; + /** * enum tdls_event_msg_type - TDLS event message type - * @TDLS_SHOULD_DISCOVER: should do discover - * @TDLS_SHOULD_TEARDOWN: notify teardown the link + * @TDLS_SHOULD_DISCOVER: should do discover for peer (based on tx bytes per + * second > tx_discover threshold) + * @TDLS_SHOULD_TEARDOWN: recommend teardown the link for peer due to tx bytes + * per second below tx_teardown_threshold * @TDLS_PEER_DISCONNECTED: tdls peer disconnected - * @TDLS_CONNECTION_TRACKER_NOTIFY: connection tracker notify + * @TDLS_CONNECTION_TRACKER_NOTIFY: TDLS/BT role change notification for + * connection tracker */ enum tdls_event_msg_type { TDLS_SHOULD_DISCOVER = 0, @@ -168,19 +223,25 @@ enum tdls_event_msg_type { /** * enum tdls_event_reason - TDLS event reason - * @TDLS_TEARDOWN_TX: - * @TDLS_TEARDOWN_RSSI: - * @TDLS_TEARDOWN_SCAN: - * @TDLS_TEARDOWN_PTR_TIMEOUT: - * @TDLS_TEARDOWN_BAD_PTR: - * @TDLS_TEARDOWN_NO_RSP: - * @TDLS_DISCONNECTED_PEER_DELETE: - * @TDLS_PEER_ENTER_BUF_STA: - * @TDLS_PEER_EXIT_BUF_STA: - * @TDLS_ENTER_BT_BUSY: - * @TDLS_EXIT_BT_BUSY: - * @DLS_SCAN_STARTED: - * @TDLS_SCAN_COMPLETED: + * @TDLS_TEARDOWN_TX: tdls teardown recommended due to low transmits + * @TDLS_TEARDOWN_RSSI: tdls link tear down recommended due to poor RSSI + * @TDLS_TEARDOWN_SCAN: tdls link tear down recommended due to offchannel scan + * @TDLS_TEARDOWN_PTR_TIMEOUT: tdls peer disconnected due to PTR timeout + * @TDLS_TEARDOWN_BAD_PTR: tdls peer disconnected due wrong PTR format + * @TDLS_TEARDOWN_NO_RSP: tdls peer not responding + * @TDLS_DISCONNECTED_PEER_DELETE: tdls peer disconnected due to peer deletion + * @TDLS_PEER_ENTER_BUF_STA: tdls entered buffer STA role, TDLS connection + * tracker needs to handle this + * @TDLS_PEER_EXIT_BUF_STA: tdls exited buffer STA role, TDLS connection tracker + * needs to handle this + * @TDLS_ENTER_BT_BUSY: BT entered busy mode, TDLS connection tracker needs to + * handle this + * @TDLS_EXIT_BT_BUSY: BT exited busy mode, TDLS connection tracker needs to + * handle this + * @DLS_SCAN_STARTED: TDLS module received a scan start event, TDLS connection + * tracker needs to handle this + * @TDLS_SCAN_COMPLETED: TDLS module received a scan complete event, TDLS + * connection tracker needs to handle this */ enum tdls_event_reason { TDLS_TEARDOWN_TX, @@ -198,6 +259,19 @@ enum tdls_event_reason { TDLS_SCAN_COMPLETED, }; +/** + * struct tdls_osif_indication - tdls indication to os if layer + * @vdev: vdev object + * @reason: used with teardown indication + * @peer_mac: MAC address of the TDLS peer + */ +struct tdls_osif_indication { + struct wlan_objmgr_vdev *vdev; + uint16_t reason; + uint8_t peer_mac[QDF_MAC_ADDR_SIZE]; + QDF_STATUS status; +}; + /** * struct tx_frame - tx frame * @buf: frame buffer @@ -268,13 +342,14 @@ enum tdls_feature_bit { * @tdls_pre_off_chan_num: tdls off channel number * @tdls_pre_off_chan_bw: tdls off channel bandwidth * @tdls_peer_kickout_threshold: sta kickout threshold for tdls peer + * @delayed_trig_framint: delayed trigger frame interval */ struct tdls_user_config { uint32_t tdls_tx_states_period; uint32_t tdls_tx_pkt_threshold; uint32_t tdls_rx_pkt_threshold; uint32_t tdls_max_discovery_attempt; - uint32_t tdls_idle_timeoute; + uint32_t tdls_idle_timeout; uint32_t tdls_idle_pkt_threshold; uint32_t tdls_rssi_trigger_threshold; uint32_t tdls_rssi_teardown_threshold; @@ -287,6 +362,7 @@ struct tdls_user_config { uint32_t tdls_pre_off_chan_num; uint32_t tdls_pre_off_chan_bw; uint32_t tdls_peer_kickout_threshold; + uint32_t delayed_trig_framint; }; /** @@ -331,32 +407,60 @@ struct tdls_tx_cnf { /* This callback is used to report state change of peer to wpa_supplicant */ typedef int (*tdls_state_change_callback)(const uint8_t *mac, - uint32_t opclass, - uint32_t channel, - uint32_t state, - int32_t reason, void *ctx); + uint32_t opclass, + uint32_t channel, + uint32_t state, + int32_t reason, void *ctx); /* This callback is used to report events to os_if layer */ -typedef QDF_STATUS (*tdls_evt_callback) (void *data, - enum tdls_event_type ev_type, - void *event); +typedef void (*tdls_evt_callback) (void *data, + enum tdls_event_type ev_type, + struct tdls_osif_indication *event); /* prototype of callback registered by hdd to receive the ack cnf */ typedef int (*tdls_tx_ack_cnf_callback)(void *user_data, - struct tdls_tx_cnf *tx_ack_cnf_cb_data); + struct tdls_tx_cnf *tx_ack_cnf_cb_data); +/* This callback is used to register TDLS peer with TL */ +typedef QDF_STATUS (*tdls_register_tl_peer_callback)(void *userdata, + uint32_t vdev_id, + const uint8_t *mac, + uint16_t stat_id, + uint8_t ucastsig, + uint8_t qos); + +/* This callback is used to deregister TDLS peer */ +typedef QDF_STATUS (*tdls_deregister_tl_peer_callback)(void *userdata, + uint32_t vdev_id, + uint8_t sta_id); /** * struct tdls_start_params - tdls start params + * @config: tdls user config + * @tdls_send_mgmt_req: pass eWNI_SME_TDLS_SEND_MGMT_REQ value + * @tdls_add_sta_req: pass eWNI_SME_TDLS_ADD_STA_REQ value + * @tdls_del_sta_req: pass eWNI_SME_TDLS_DEL_STA_REQ value + * @tdls_update_peer_state: pass WMA_UPDATE_TDLS_PEER_STATE value * @tdls_event_cb: tdls event callback * @tdls_evt_cb_data: tdls event data * @ack_cnf_cb: tdls tx ack callback to indicate the tx status * @tx_ack_cnf_cb_data: tdls tx ack user data + * @tdls_reg_tl_peer: tdls register tdls peer + * @tdls_dereg_tl_peer: tdls deregister tdls peer + * @tdls_tl_peer_data: userdata for register/deregister TDLS peer */ struct tdls_start_params { + struct tdls_user_config config; + uint16_t tdls_send_mgmt_req; + uint16_t tdls_add_sta_req; + uint16_t tdls_del_sta_req; + uint16_t tdls_update_peer_state; tdls_evt_callback tdls_event_cb; void *tdls_evt_cb_data; tdls_tx_ack_cnf_callback ack_cnf_cb; void *tx_ack_cnf_cb_data; + tdls_register_tl_peer_callback tdls_reg_tl_peer; + tdls_deregister_tl_peer_callback tdls_dereg_tl_peer; + void *tdls_tl_peer_data; }; /** @@ -366,7 +470,7 @@ struct tdls_start_params { * @vdev_id: vdev id */ struct tdls_add_peer_params { - const uint8_t *peer_addr; + uint8_t peer_addr[QDF_MAC_ADDR_SIZE]; uint32_t peer_type; uint32_t vdev_id; }; @@ -437,7 +541,7 @@ struct vhtcap { }; struct tdls_update_peer_params { - const uint8_t *peer_addr; + uint8_t peer_addr[QDF_MAC_ADDR_SIZE]; uint32_t peer_type; uint32_t vdev_id; uint16_t capability; @@ -454,6 +558,7 @@ struct tdls_update_peer_params { uint8_t supported_channels[WLAN_MAC_MAX_SUPP_CHANNELS]; uint8_t supported_oper_classes_len; uint8_t supported_oper_classes[WLAN_MAX_SUPP_OPER_CLASSES]; + bool is_qos_wmm_sta; }; struct tdls_update_peer_request { @@ -461,6 +566,36 @@ struct tdls_update_peer_request { struct tdls_update_peer_params update_peer_req; }; +/** + * struct tdls_oper_request - tdls operation request + * @vdev: vdev object + * @peer_addr: MAC address of the TDLS peer + */ +struct tdls_oper_request { + struct wlan_objmgr_vdev *vdev; + uint8_t peer_addr[QDF_MAC_ADDR_SIZE]; +}; + +/** + * struct tdls_oper_config_force_peer_request - tdls enable force peer request + * @vdev: vdev object + * @peer_addr: MAC address of the TDLS peer + * @chan: channel + * @max_latency: maximum latency + * @op_class: operation class + * @min_bandwidth: minimal bandwidth + * @callback: state change callback + */ +struct tdls_oper_config_force_peer_request { + struct wlan_objmgr_vdev *vdev; + uint8_t peer_addr[QDF_MAC_ADDR_SIZE]; + uint32_t chan; + uint32_t max_latency; + uint32_t op_class; + uint32_t min_bandwidth; + tdls_state_change_callback callback; +}; + /** * struct tdls_info - tdls info * @@ -550,20 +685,22 @@ struct tdls_peer_params { }; /** - * struct tdls_peer_update_params - TDLS peer state parameters + * struct tdls_peer_update_state - TDLS peer state parameters * @vdev_id: vdev id * @peer_macaddr: peer mac address * @peer_cap: peer capabality + * @resp_reqd: response needed */ -struct tdls_peer_update_params { +struct tdls_peer_update_state { uint32_t vdev_id; uint8_t peer_macaddr[QDF_MAC_ADDR_SIZE]; uint32_t peer_state; struct tdls_peer_params peer_cap; + bool resp_reqd; }; /** - * struct tdls_chan_switch_params - channel switch parameter structure + * struct tdls_channel_switch_params - channel switch parameter structure * @vdev_id: vdev ID * @peer_mac_addr: Peer mac address * @tdls_off_ch_bw_offset: Target off-channel bandwitdh offset @@ -571,7 +708,7 @@ struct tdls_peer_update_params { * @oper_class: Operating class for target channel * @is_responder: Responder or initiator */ -struct tdls_chan_switch_params { +struct tdls_channel_switch_params { uint32_t vdev_id; uint8_t peer_mac_addr[QDF_MAC_ADDR_SIZE]; uint16_t tdls_off_ch_bw_offset; diff --git a/umac/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h b/umac/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h index 02d8c2beff03..052f55b09426 100644 --- a/umac/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h +++ b/umac/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h @@ -28,6 +28,9 @@ #include #include #include +#include +#include +#include /** * ucfg_tdls_init() - TDLS module initialization API @@ -66,8 +69,9 @@ QDF_STATUS ucfg_tdls_psoc_close(struct wlan_objmgr_psoc *psoc); * * Return: QDF_STATUS */ -QDF_STATUS ucfg_tdls_psoc_start(struct wlan_objmgr_psoc *psoc, - struct tdls_start_params *req); +QDF_STATUS ucfg_tdls_update_config(struct wlan_objmgr_psoc *psoc, + struct tdls_start_params *req); + /** * ucfg_tdls_psoc_stop() - TDLS module stop * @psoc: psoc object @@ -75,4 +79,24 @@ QDF_STATUS ucfg_tdls_psoc_start(struct wlan_objmgr_psoc *psoc, * Return: QDF_STATUS */ QDF_STATUS ucfg_tdls_psoc_stop(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_tdls_add_peer() - handle TDLS add peer + * @vdev: vdev object + * @add_peer_req: add peer request parameters + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_add_peer(struct wlan_objmgr_vdev *vdev, + struct tdls_add_peer_params *add_peer_req); + +/** + * ucfg_tdls_update_peer() - handle TDLS update peer + * @vdev: vdev object + * @update_peer: update TDLS request parameters + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_update_peer(struct wlan_objmgr_vdev *vdev, + struct tdls_update_peer_params *update_peer); #endif diff --git a/umac/tdls/dispatcher/src/wlan_tdls_ucfg_api.c b/umac/tdls/dispatcher/src/wlan_tdls_ucfg_api.c index 3d49c5adac75..045d99d1e91d 100644 --- a/umac/tdls/dispatcher/src/wlan_tdls_ucfg_api.c +++ b/umac/tdls/dispatcher/src/wlan_tdls_ucfg_api.c @@ -24,6 +24,7 @@ #include #include "../../core/src/wlan_tdls_main.h" +#include "../../core/src/wlan_tdls_cmds_process.h" #include #include @@ -113,6 +114,7 @@ QDF_STATUS ucfg_tdls_deinit(void) static QDF_STATUS tdls_global_init(struct tdls_soc_priv_obj *soc_obj) { uint8_t sta_idx; + uint32_t feature; soc_obj->connected_peer_count = 0; soc_obj->tdls_nss_switch_in_progress = false; @@ -120,7 +122,16 @@ static QDF_STATUS tdls_global_init(struct tdls_soc_priv_obj *soc_obj) soc_obj->tdls_nss_teardown_complete = false; soc_obj->tdls_nss_transition_mode = TDLS_NSS_TRANSITION_S_UNKNOWN; soc_obj->tdls_user_config_mode = TDLS_SUPPORT_DISABLED; - soc_obj->max_num_tdls_sta = WLAN_TDLS_STA_MAX_NUM; + + feature = soc_obj->tdls_configs.tdls_feature_flags; + if (TDLS_IS_BUFFER_STA_ENABLED(feature) || + TDLS_IS_SLEEP_STA_ENABLED(feature) || + TDLS_IS_OFF_CHANNEL_ENABLED(feature)) + soc_obj->max_num_tdls_sta = + WLAN_TDLS_STA_P_UAPSD_OFFCHAN_MAX_NUM; + else + soc_obj->max_num_tdls_sta = WLAN_TDLS_STA_MAX_NUM; + for (sta_idx = 0; sta_idx < soc_obj->max_num_tdls_sta; sta_idx++) { soc_obj->tdls_conn_info[sta_idx].sta_id = 0; @@ -142,7 +153,7 @@ QDF_STATUS ucfg_tdls_psoc_open(struct wlan_objmgr_psoc *psoc) tdls_notice("tdls psoc open"); soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_TDLS); - if (soc_obj == NULL) { + if (!soc_obj) { tdls_err("Failed to get tdls psoc component"); return QDF_STATUS_E_FAILURE; } @@ -152,15 +163,21 @@ QDF_STATUS ucfg_tdls_psoc_open(struct wlan_objmgr_psoc *psoc) return status; } -QDF_STATUS ucfg_tdls_psoc_start(struct wlan_objmgr_psoc *psoc, - struct tdls_start_params *req) +QDF_STATUS ucfg_tdls_update_config(struct wlan_objmgr_psoc *psoc, + struct tdls_start_params *req) { struct tdls_soc_priv_obj *soc_obj; + uint32_t tdls_feature_flags; + + tdls_notice("tdls update config "); + if (!psoc || !req) { + tdls_err("psoc: 0x%p, req: 0x%p", psoc, req); + return QDF_STATUS_E_FAILURE; + } - tdls_notice("tdls psoc start"); soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_TDLS); - if (soc_obj == NULL) { + if (!soc_obj) { tdls_err("Failed to get tdls psoc component"); return QDF_STATUS_E_FAILURE; } @@ -171,6 +188,28 @@ QDF_STATUS ucfg_tdls_psoc_start(struct wlan_objmgr_psoc *psoc, soc_obj->tdls_tx_cnf_cb = req->ack_cnf_cb; soc_obj->tx_ack_cnf_cb_data = req->tx_ack_cnf_cb_data; + /* Save callbacks to register/deregister TDLS sta with TL */ + soc_obj->tdls_reg_tl_peer = req->tdls_reg_tl_peer; + soc_obj->tdls_dereg_tl_peer = req->tdls_dereg_tl_peer; + soc_obj->tdls_tl_peer_data = req->tdls_tl_peer_data; + + /* Save legacy PE/WMA commands in TDLS soc object */ + soc_obj->tdls_send_mgmt_req = req->tdls_send_mgmt_req; + soc_obj->tdls_add_sta_req = req->tdls_add_sta_req; + soc_obj->tdls_del_sta_req = req->tdls_del_sta_req; + soc_obj->tdls_update_peer_state = req->tdls_update_peer_state; + + /* Update TDLS user config */ + qdf_mem_copy(&soc_obj->tdls_configs, &req->config, sizeof(req->config)); + tdls_feature_flags = soc_obj->tdls_configs.tdls_feature_flags; + + if (!TDLS_IS_IMPLICIT_TRIG_ENABLED(tdls_feature_flags)) + soc_obj->tdls_current_mode = TDLS_SUPPORT_EXP_TRIG_ONLY; + else if (TDLS_IS_EXTERNAL_CONTROL_ENABLED(tdls_feature_flags)) + soc_obj->tdls_current_mode = TDLS_SUPPORT_EXT_CONTROL; + else + soc_obj->tdls_current_mode = TDLS_SUPPORT_IMP_MODE; + return QDF_STATUS_SUCCESS; } @@ -181,7 +220,7 @@ QDF_STATUS ucfg_tdls_psoc_stop(struct wlan_objmgr_psoc *psoc) tdls_notice("tdls psoc stop"); soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_TDLS); - if (soc_obj == NULL) { + if (!soc_obj) { tdls_err("Failed to get tdls psoc component"); return QDF_STATUS_E_FAILURE; } @@ -203,3 +242,93 @@ QDF_STATUS ucfg_tdls_psoc_close(struct wlan_objmgr_psoc *psoc) return status; } + +QDF_STATUS ucfg_tdls_add_peer(struct wlan_objmgr_vdev *vdev, + struct tdls_add_peer_params *add_peer_req) +{ + struct scheduler_msg msg = {0, }; + struct tdls_add_peer_request *req; + QDF_STATUS status; + + if (!vdev || !add_peer_req) { + tdls_err("vdev: %p, req %p", vdev, add_peer_req); + return QDF_STATUS_E_NULL_VALUE; + } + tdls_debug("vdevid: %d, peertype: %d", + add_peer_req->vdev_id, add_peer_req->peer_type); + + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev"); + return status; + } + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + tdls_err("mem allocate fail"); + status = QDF_STATUS_E_NOMEM; + goto dec_ref; + } + + qdf_mem_copy(&req->add_peer_req, add_peer_req, sizeof(*add_peer_req)); + req->vdev = vdev; + + msg.bodyptr = req; + msg.callback = tdls_process_cmd; + msg.type = TDLS_CMD_ADD_STA; + status = scheduler_post_msg(QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("post add peer msg fail"); + qdf_mem_free(req); + goto dec_ref; + } + + return status; +dec_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + return status; +} + +QDF_STATUS ucfg_tdls_update_peer(struct wlan_objmgr_vdev *vdev, + struct tdls_update_peer_params *update_peer) +{ + struct scheduler_msg msg = {0,}; + struct tdls_update_peer_request *req; + QDF_STATUS status; + + if (!vdev || !update_peer) { + tdls_err("vdev: %p, update_peer: %p", vdev, update_peer); + return QDF_STATUS_E_NULL_VALUE; + } + + tdls_debug("vdev_id: %d, peertype: %d", + update_peer->vdev_id, update_peer->peer_type); + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev"); + return status; + } + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + tdls_err("mem allocate fail"); + status = QDF_STATUS_E_NOMEM; + goto dec_ref; + } + qdf_mem_copy(&req->update_peer_req, update_peer, sizeof(*update_peer)); + req->vdev = vdev; + + msg.bodyptr = req; + msg.callback = tdls_process_cmd; + msg.type = TDLS_CMD_CHANGE_STA; + status = scheduler_post_msg(QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("post update peer msg fail"); + qdf_mem_free(req); + goto dec_ref; + } + + return status; +dec_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + return status; +}