diff --git a/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_api.h b/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_api.h new file mode 100644 index 000000000000..f39b636346ed --- /dev/null +++ b/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_api.h @@ -0,0 +1,2781 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_POLICY_MGR_API_H +#define __WLAN_POLICY_MGR_API_H + +/** + * DOC: wlan_policy_mgr_api.h + * + * Concurrenct Connection Management entity + */ + +/* Include files */ +#include "qdf_types.h" +#include "qdf_status.h" +#include "wlan_objmgr_psoc_obj.h" +#include "wlan_policy_mgr_public_struct.h" + +struct target_psoc_info; + +typedef const enum policy_mgr_pcl_type + pm_dbs_pcl_second_connection_table_type + [PM_MAX_ONE_CONNECTION_MODE][PM_MAX_NUM_OF_MODE] + [PM_MAX_CONC_PRIORITY_MODE]; + +typedef const enum policy_mgr_pcl_type + pm_dbs_pcl_third_connection_table_type + [PM_MAX_TWO_CONNECTION_MODE][PM_MAX_NUM_OF_MODE] + [PM_MAX_CONC_PRIORITY_MODE]; + +typedef const enum policy_mgr_conc_next_action + policy_mgr_next_action_two_connection_table_type + [PM_MAX_ONE_CONNECTION_MODE][POLICY_MGR_MAX_BAND]; + +typedef const enum policy_mgr_conc_next_action + policy_mgr_next_action_three_connection_table_type + [PM_MAX_TWO_CONNECTION_MODE][POLICY_MGR_MAX_BAND]; + +#define PM_FW_MODE_STA_STA_BIT_POS 0 +#define PM_FW_MODE_STA_P2P_BIT_POS 1 + +#define PM_FW_MODE_STA_STA_BIT_MASK (0x1 << PM_FW_MODE_STA_STA_BIT_POS) +#define PM_FW_MODE_STA_P2P_BIT_MASK (0x1 << PM_FW_MODE_STA_P2P_BIT_POS) + +#define PM_CHANNEL_SELECT_LOGIC_STA_STA_GET(channel_select_logic_conc) \ + ((channel_select_logic_conc & PM_FW_MODE_STA_STA_BIT_MASK) >> \ + PM_FW_MODE_STA_STA_BIT_POS) +#define PM_CHANNEL_SELECT_LOGIC_STA_P2P_GET(channel_select_logic_conc) \ + ((channel_select_logic_conc & PM_FW_MODE_STA_P2P_BIT_MASK) >> \ + PM_FW_MODE_STA_P2P_BIT_POS) + +/** + * policy_mgr_get_mcc_scc_switch() - To mcc to scc switch setting from INI + * @psoc: pointer to psoc + * @mcc_scc_switch: value to be filled + * + * This API pulls mcc to scc switch setting which is given as part of INI and + * stored in policy manager's CFGs. + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_mcc_scc_switch(struct wlan_objmgr_psoc *psoc, + uint8_t *mcc_scc_switch); +/** + * policy_mgr_get_sys_pref() - to get system preference + * @psoc: pointer to psoc + * @sys_pref: value to be filled + * + * This API pulls the system preference for policy manager to provide + * PCL + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_sys_pref(struct wlan_objmgr_psoc *psoc, + uint8_t *sys_pref); +/** + * policy_mgr_set_sys_pref() - to set system preference + * @psoc: pointer to psoc + * @sys_pref: value to be applied as new INI setting + * + * This API is meant to override original INI setting for system pref + * with new value which is used by policy manager to provide PCL + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_set_sys_pref(struct wlan_objmgr_psoc *psoc, + uint8_t sys_pref); +/** + * policy_mgr_get_max_conc_cxns() - to get max num of conc connections + * @psoc: pointer to psoc + * @max_conc_cxns: value to be filled + * + * This API pulls max number of active connections which can be allowed + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_max_conc_cxns(struct wlan_objmgr_psoc *psoc, + uint8_t *max_conc_cxns); +/** + * policy_mgr_get_conc_rule1() - to find out if conc rule1 is enabled + * @psoc: pointer to psoc + * @conc_rule1: value to be filled + * + * This API is used to find out if conc rule-1 is enabled by user + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_conc_rule1(struct wlan_objmgr_psoc *psoc, + uint8_t *conc_rule1); +/** + * policy_mgr_get_conc_rule2() - to find out if conc rule2 is enabled + * @psoc: pointer to psoc + * @conc_rule2: value to be filled + * + * This API is used to find out if conc rule-2 is enabled by user + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_conc_rule2(struct wlan_objmgr_psoc *psoc, + uint8_t *conc_rule2); +/** + * policy_mgr_get_dbs_selection_plcy() - DBS HW mode selection setting + * @psoc: pointer to psoc + * @dbs_selection_plcy: value to be filled + * + * This API is used to find out DBS HW mode preference. + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_dbs_selection_plcy(struct wlan_objmgr_psoc *psoc, + uint32_t *dbs_selection_plcy); +/** + * policy_mgr_get_vdev_priority_list() - to get vdev priority list + * @psoc: pointer to psoc + * @vdev_priority_list: value to be filled + * + * This API is used to find out vdev_priority_list setting + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_vdev_priority_list(struct wlan_objmgr_psoc *psoc, + uint32_t *vdev_priority_list); +/** + * policy_mgr_get_chnl_select_plcy() - to get channel selection policy + * @psoc: pointer to psoc + * @chnl_select_plcy: value to be filled + * + * This API is used to find out which channel selection policy has been + * configured + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_chnl_select_plcy(struct wlan_objmgr_psoc *psoc, + uint32_t *chnl_select_plcy); + +/** + * policy_mgr_get_mcc_adaptive_sch() - to get mcc adaptive scheduler + * @psoc: pointer to psoc + * @enable_mcc_adaptive_sch: value to be filled + * + * This API is used to find out if mcc adaptive scheduler enabled or disabled + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_mcc_adaptive_sch(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_mcc_adaptive_sch); + +/** + * policy_mgr_get_sta_cxn_5g_band() - to get STA's connection in 5G config + * + * @psoc: pointer to psoc + * @enable_sta_cxn_5g_band: value to be filled + * + * This API is used to find out if STA connection in 5G band is allowed or + * disallowed. + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS policy_mgr_get_sta_cxn_5g_band(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_sta_cxn_5g_band); +/** + * policy_mgr_set_concurrency_mode() - To set concurrency mode + * @psoc: PSOC object data + * @mode: device mode + * + * This routine is called to set the concurrency mode + * + * Return: NONE + */ +void policy_mgr_set_concurrency_mode(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode); + +/** + * policy_mgr_clear_concurrency_mode() - To clear concurrency mode + * @psoc: PSOC object data + * @mode: device mode + * + * This routine is called to clear the concurrency mode + * + * Return: NONE + */ +void policy_mgr_clear_concurrency_mode(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode); + +/** + * policy_mgr_get_connection_count() - provides the count of + * current connections + * @psoc: PSOC object information + * + * This function provides the count of current connections + * + * Return: connection count + */ +uint32_t policy_mgr_get_connection_count(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_concurrency_mode() - return concurrency mode + * @psoc: PSOC object information + * + * This routine is used to retrieve concurrency mode + * + * Return: uint32_t value of concurrency mask + */ +uint32_t policy_mgr_get_concurrency_mode(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_search_and_check_for_session_conc() - Checks if + * concurrecy is allowed + * @psoc: PSOC object information + * @session_id: Session id + * @roam_profile: Pointer to the roam profile + * + * Searches and gets the channel number from the scan results and checks if + * concurrency is allowed for the given session ID + * + * Non zero channel number if concurrency is allowed, zero otherwise + */ +uint8_t policy_mgr_search_and_check_for_session_conc( + struct wlan_objmgr_psoc *psoc, + uint8_t session_id, void *roam_profile); + +/** + * policy_mgr_is_chnl_in_diff_band() - to check that given channel + * is in diff band from existing channel or not + * @psoc: pointer to psoc + * @channel: given channel + * + * This API will check that if the passed channel is in diff band than the + * already existing connections or not. + * + * Return: true if channel is in diff band + */ +bool policy_mgr_is_chnl_in_diff_band(struct wlan_objmgr_psoc *psoc, + uint8_t channel); + +/** + * policy_mgr_check_for_session_conc() - Check if concurrency is + * allowed for a session + * @psoc: PSOC object information + * @session_id: Session ID + * @channel: Channel number + * + * Checks if connection is allowed for a given session_id + * + * True if the concurrency is allowed, false otherwise + */ +bool policy_mgr_check_for_session_conc( + struct wlan_objmgr_psoc *psoc, uint8_t session_id, uint8_t channel); + +/** + * policy_mgr_handle_conc_multiport() - to handle multiport concurrency + * @session_id: Session ID + * @channel: Channel number + * + * This routine will handle STA side concurrency when policy manager + * is enabled. + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_handle_conc_multiport( + struct wlan_objmgr_psoc *psoc, uint8_t session_id, uint8_t channel); + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +/** + * policy_mgr_check_concurrent_intf_and_restart_sap() - Check + * concurrent change intf + * @psoc: PSOC object information + * @operation_channel: operation channel + * @vdev_id: vdev id of SAP + * + * Checks the concurrent change interface and restarts SAP + * + * Return: None + */ +void policy_mgr_check_concurrent_intf_and_restart_sap( + struct wlan_objmgr_psoc *psoc); +#else +static inline void policy_mgr_check_concurrent_intf_and_restart_sap( + struct wlan_objmgr_psoc *psoc) +{ + +} +#endif /* FEATURE_WLAN_MCC_TO_SCC_SWITCH */ + +/** + * policy_mgr_is_mcc_in_24G() - Function to check for MCC in 2.4GHz + * @psoc: PSOC object information + * + * This function is used to check for MCC operation in 2.4GHz band. + * STA, P2P and SAP adapters are only considered. + * + * Return: True if mcc is detected in 2.4 Ghz, false otherwise + * + */ +bool policy_mgr_is_mcc_in_24G(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_change_mcc_go_beacon_interval() - Change MCC beacon interval + * @psoc: PSOC object information + * @vdev_id: vdev id + * @dev_mode: device mode + * + * Updates the beacon parameters of the GO in MCC scenario + * + * Return: Success or Failure depending on the overall function behavior + */ +QDF_STATUS policy_mgr_change_mcc_go_beacon_interval( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, enum QDF_OPMODE dev_mode); + +#if defined(FEATURE_WLAN_MCC_TO_SCC_SWITCH) +/** + * policy_mgr_change_sap_channel_with_csa() - Move SAP channel using (E)CSA + * @psoc: PSOC object information + * @vdev_id: Vdev id + * @channel: Channel to change + * @ch_width: channel width to change + * @forced: Force to switch channel, ignore SCC/MCC check + * + * Invoke the callback function to change SAP channel using (E)CSA + * + * Return: None + */ +void policy_mgr_change_sap_channel_with_csa( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint32_t channel, + uint32_t ch_width, + bool forced); +#else +static inline void policy_mgr_change_sap_channel_with_csa( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint32_t channel, + uint32_t ch_width, + bool forced) +{ + +} +#endif + +/** + * policy_mgr_set_pcl_for_existing_combo() - SET PCL for existing combo + * @psoc: PSOC object information + * @mode: Adapter mode + * + * Return: None + */ +void policy_mgr_set_pcl_for_existing_combo(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode); +/** + * policy_mgr_incr_active_session() - increments the number of active sessions + * @psoc: PSOC object information + * @mode: Adapter mode + * @session_id: session ID for the connection session + * + * This function increments the number of active sessions maintained per device + * mode. In the case of STA/P2P CLI/IBSS upon connection indication it is + * incremented; In the case of SAP/P2P GO upon bss start it is incremented + * + * Return: None + */ +void policy_mgr_incr_active_session(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode, uint8_t session_id); + +/** + * policy_mgr_decr_active_session() - decrements the number of active sessions + * @psoc: PSOC object information + * @mode: Adapter mode + * @session_id: session ID for the connection session + * + * This function decrements the number of active sessions maintained per device + * mode. In the case of STA/P2P CLI/IBSS upon disconnection it is decremented + * In the case of SAP/P2P GO upon bss stop it is decremented + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_decr_active_session(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode, uint8_t session_id); + +/** + * policy_mgr_decr_session_set_pcl() - Decrement session count and set PCL + * @psoc: PSOC object information + * @mode: Adapter mode + * @session_id: Session id + * + * Decrements the active session count and sets the PCL if a STA connection + * exists + * + * Return: None + */ +void policy_mgr_decr_session_set_pcl(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode, uint8_t session_id); + +/** + * policy_mgr_get_channel() - provide channel number of given mode and vdevid + * @psoc: PSOC object information + * @mode: given mode + * @vdev_id: pointer to vdev_id + * + * This API will provide channel number of matching mode and vdevid. + * If vdev_id is NULL then it will match only mode + * If vdev_id is not NULL the it will match both mode and vdev_id + * + * Return: channel number + */ +uint8_t policy_mgr_get_channel(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, uint32_t *vdev_id); + +/** + * policy_mgr_get_pcl() - provides the preferred channel list for + * new connection + * @psoc: PSOC object information + * @mode: Device mode + * @pcl_channels: PCL channels + * @len: length of the PCL + * @pcl_weight: Weights of the PCL + * @weight_len: Max length of the weights list + * + * This function provides the preferred channel list on which + * policy manager wants the new connection to come up. Various + * connection decision making entities will using this function + * to query the PCL info + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_pcl(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint8_t *pcl_channels, uint32_t *len, + uint8_t *pcl_weight, uint32_t weight_len); + +/** + * policy_mgr_update_with_safe_channel_list() - provides the safe + * channel list + * @psoc: PSOC object information + * @pcl_channels: channel list + * @len: length of the list + * @weight_list: Weights of the PCL + * @weight_len: Max length of the weights list + * + * This function provides the safe channel list from the list + * provided after consulting the channel avoidance list + * + * Return: None + */ +void policy_mgr_update_with_safe_channel_list(struct wlan_objmgr_psoc *psoc, + uint8_t *pcl_channels, uint32_t *len, + uint8_t *weight_list, uint32_t weight_len); + +/** + * policy_mgr_get_nondfs_preferred_channel() - to get non-dfs preferred channel + * for given mode + * @psoc: PSOC object information + * @mode: mode for which preferred non-dfs channel is requested + * @for_existing_conn: flag to indicate if preferred channel is requested + * for existing connection + * + * this routine will return non-dfs channel + * 1) for getting non-dfs preferred channel, first we check if there are any + * other connection exist whose channel is non-dfs. if yes then return that + * channel so that we can accommodate upto 3 mode concurrency. + * 2) if there no any other connection present then query concurrency module + * to give preferred channel list. once we get preferred channel list, loop + * through list to find first non-dfs channel from ascending order. + * + * Return: uint8_t non-dfs channel + */ +uint8_t policy_mgr_get_nondfs_preferred_channel(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, bool for_existing_conn); + +/** + * policy_mgr_is_any_nondfs_chnl_present() - Find any non-dfs + * channel from conc table + * @psoc: PSOC object information + * @channel: pointer to channel which needs to be filled + * + * In-case if any connection is already present whose channel is none dfs then + * return that channel + * + * Return: true up-on finding non-dfs channel else false + */ +bool policy_mgr_is_any_nondfs_chnl_present(struct wlan_objmgr_psoc *psoc, + uint8_t *channel); + +/** + * policy_mgr_is_any_dfs_beaconing_session_present() - to find + * if any DFS session + * @psoc: PSOC object information + * @channel: pointer to channel number that needs to filled + * + * If any beaconing session such as SAP or GO present and it is on DFS channel + * then this function will return true + * + * Return: true if session is on DFS or false if session is on non-dfs channel + */ +bool policy_mgr_is_any_dfs_beaconing_session_present( + struct wlan_objmgr_psoc *psoc, uint8_t *channel); + +/** + * policy_mgr_allow_concurrency() - Check for allowed concurrency + * combination consulting the PCL + * @psoc: PSOC object information + * @mode: new connection mode + * @channel: channel on which new connection is coming up + * @bw: Bandwidth requested by the connection (optional) + * + * When a new connection is about to come up check if current + * concurrency combination including the new connection is + * allowed or not based on the HW capability + * + * Return: True/False + */ +bool policy_mgr_allow_concurrency(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint8_t channel, enum hw_mode_bandwidth bw); + +/** + * policy_mgr_allow_concurrency_csa() - Check for allowed concurrency + * combination when channel switch + * @psoc: PSOC object information + * @mode: connection mode + * @channel: target channel to switch + * @vdev_id: vdev id of channel switch interface + * + * There is already existing SAP+GO combination but due to upper layer + * notifying LTE-COEX event or sending command to move one of the connections + * to different channel. In such cases before moving existing connection to new + * channel, check if new channel can co-exist with the other existing + * connection. For example, one SAP1 is on channel-6 and second SAP2 is on + * channel-36 and lets say they are doing DBS, and lets say upper layer sends + * LTE-COEX to move SAP1 from channel-6 to channel-149. In this case, SAP1 and + * SAP2 will end up doing MCC which may not be desirable result. such cases + * will be prevented with this API. + * + * Return: True/False + */ +bool policy_mgr_allow_concurrency_csa(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint8_t channel, + uint32_t vdev_id); + +/** + * policy_mgr_get_first_connection_pcl_table_index() - provides the + * row index to firstConnectionPclTable to get to the correct + * pcl + * @psoc: PSOC object information + * + * This function provides the row index to + * firstConnectionPclTable. The index is the preference config. + * + * Return: table index + */ +enum policy_mgr_conc_priority_mode + policy_mgr_get_first_connection_pcl_table_index( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_second_connection_pcl_table_index() - provides the + * row index to secondConnectionPclTable to get to the correct + * pcl + * @psoc: PSOC object information + * + * This function provides the row index to + * secondConnectionPclTable. The index is derived based on + * current connection, band on which it is on & chain mask it is + * using, as obtained from pm_conc_connection_list. + * + * Return: table index + */ +enum policy_mgr_one_connection_mode + policy_mgr_get_second_connection_pcl_table_index( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_third_connection_pcl_table_index() - provides the + * row index to thirdConnectionPclTable to get to the correct + * pcl + * @psoc: PSOC object information + * + * This function provides the row index to + * thirdConnectionPclTable. The index is derived based on + * current connection, band on which it is on & chain mask it is + * using, as obtained from pm_conc_connection_list. + * + * Return: table index + */ +enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_incr_connection_count() - adds the new connection to + * the current connections list + * @psoc: PSOC object information + * @vdev_id: vdev id + * + * + * This function adds the new connection to the current + * connections list + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_incr_connection_count(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id); + +/** + * policy_mgr_update_connection_info() - updates the existing + * connection in the current connections list + * @psoc: PSOC object information + * @vdev_id: vdev id + * + * + * This function adds the new connection to the current + * connections list + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_update_connection_info(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id); + +/** + * policy_mgr_decr_connection_count() - remove the old connection + * from the current connections list + * @psoc: PSOC object information + * @vdev_id: vdev id of the old connection + * + * + * This function removes the old connection from the current + * connections list + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_decr_connection_count(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id); + +/** + * policy_mgr_current_connections_update() - initiates actions + * needed on current connections once channel has been decided + * for the new connection + * @psoc: PSOC object information + * @session_id: Session id + * @channel: Channel on which new connection will be + * @reason: Reason for which connection update is required + * + * This function initiates initiates actions + * needed on current connections once channel has been decided + * for the new connection. Notifies UMAC & FW as well + * + * Return: QDF_STATUS enum + */ +QDF_STATUS policy_mgr_current_connections_update(struct wlan_objmgr_psoc *psoc, + uint32_t session_id, uint8_t channel, + enum policy_mgr_conn_update_reason); + +/** + * policy_mgr_is_dbs_allowed_for_concurrency() - If dbs is allowed for current + * concurreny + * @new_conn_mode: new connection mode + * + * When a new connection is about to come up, check if dbs is allowed for + * STA+STA or STA+P2P + * + * Return: true if dbs is allowed for STA+STA or STA+P2P else false + */ +bool policy_mgr_is_dbs_allowed_for_concurrency( + struct wlan_objmgr_psoc *psoc, enum QDF_OPMODE new_conn_mode); + +/** + * policy_mgr_get_preferred_dbs_action_table() - get dbs action table type + * @psoc: Pointer to psoc + * @vdev_id: vdev Id + * @channel: channel of vdev. + * @reason: reason of request + * + * 1. Based on band preferred and vdev priority setting to choose the preferred + * dbs action. + * 2. This routine will be used to get DBS switching action tables. + * In Genoa, two action tables for DBS1 (2x2 5G + 1x1 2G), DBS2 + * (2x2 2G + 1x1 5G). + * 3. It can be used in mode change case in CSA channel switching or Roaming, + * opportunistic upgrade. If needs switch to DBS, we needs to query this + * function to get preferred DBS mode. + * 4. This is mainly used for dual dbs mode HW. For Legacy HW, there is + * only single DBS mode. This function will return PM_NOP. + * + * return : PM_NOP, PM_DBS1, PM_DBS2 + */ +enum policy_mgr_conc_next_action +policy_mgr_get_preferred_dbs_action_table( + struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + uint8_t channel, + enum policy_mgr_conn_update_reason reason); + +/** + * policy_mgr_is_ibss_conn_exist() - to check if IBSS connection already present + * @psoc: PSOC object information + * @ibss_channel: pointer to ibss channel which needs to be filled + * + * this routine will check if IBSS connection already exist or no. If it + * exist then this routine will return true and fill the ibss_channel value. + * + * Return: true if ibss connection exist else false + */ +bool policy_mgr_is_ibss_conn_exist(struct wlan_objmgr_psoc *psoc, + uint8_t *ibss_channel); + +/** + * policy_mgr_get_conn_info() - get the current connections list + * @len: length of the list + * + * This function returns a pointer to the current connections + * list + * + * Return: pointer to connection list + */ +struct policy_mgr_conc_connection_info *policy_mgr_get_conn_info( + uint32_t *len); +#ifdef MPC_UT_FRAMEWORK +/** + * policy_mgr_incr_connection_count_utfw() - adds the new + * connection to the current connections list + * @psoc: PSOC object information + * @vdev_id: vdev id + * @tx_streams: number of transmit spatial streams + * @rx_streams: number of receive spatial streams + * @chain_mask: chain mask + * @type: connection type + * @sub_type: connection subtype + * @channelid: channel number + * @mac_id: mac id + * + * This function adds the new connection to the current + * connections list + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_incr_connection_count_utfw(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, uint32_t tx_streams, uint32_t rx_streams, + uint32_t chain_mask, uint32_t type, uint32_t sub_type, + uint32_t channelid, uint32_t mac_id); + +/** + * policy_mgr_update_connection_info_utfw() - updates the + * existing connection in the current connections list + * @psoc: PSOC object information + * @vdev_id: vdev id + * @tx_streams: number of transmit spatial streams + * @rx_streams: number of receive spatial streams + * @chain_mask: chain mask + * @type: connection type + * @sub_type: connection subtype + * @channelid: channel number + * @mac_id: mac id + * + * This function updates the connection to the current + * connections list + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_update_connection_info_utfw(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, uint32_t tx_streams, uint32_t rx_streams, + uint32_t chain_mask, uint32_t type, uint32_t sub_type, + uint32_t channelid, uint32_t mac_id); + +/** + * policy_mgr_decr_connection_count_utfw() - remove the old + * connection from the current connections list + * @psoc: PSOC object information + * @del_all: delete all entries + * @vdev_id: vdev id + * + * This function removes the old connection from the current + * connections list + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_decr_connection_count_utfw(struct wlan_objmgr_psoc *psoc, + uint32_t del_all, uint32_t vdev_id); + +/** + * policy_mgr_get_pcl_from_first_conn_table() - Get PCL for new + * connection from first connection table + * @type: Connection mode of type 'policy_mgr_con_mode' + * @sys_pref: System preference + * + * Get the PCL for a new connection + * + * Return: PCL channels enum + */ +enum policy_mgr_pcl_type policy_mgr_get_pcl_from_first_conn_table( + enum policy_mgr_con_mode type, + enum policy_mgr_conc_priority_mode sys_pref); + +/** + * policy_mgr_get_pcl_from_second_conn_table() - Get PCL for new + * connection from second connection table + * @idx: index into first connection table + * @type: Connection mode of type 'policy_mgr_con_mode' + * @sys_pref: System preference + * @dbs_capable: if HW DBS capable + * + * Get the PCL for a new connection + * + * Return: PCL channels enum + */ +enum policy_mgr_pcl_type policy_mgr_get_pcl_from_second_conn_table( + enum policy_mgr_one_connection_mode idx, enum policy_mgr_con_mode type, + enum policy_mgr_conc_priority_mode sys_pref, uint8_t dbs_capable); + +/** + * policy_mgr_get_pcl_from_third_conn_table() - Get PCL for new + * connection from third connection table + * @idx: index into second connection table + * @type: Connection mode of type 'policy_mgr_con_mode' + * @sys_pref: System preference + * @dbs_capable: if HW DBS capable + * + * Get the PCL for a new connection + * + * Return: PCL channels enum + */ +enum policy_mgr_pcl_type policy_mgr_get_pcl_from_third_conn_table( + enum policy_mgr_two_connection_mode idx, enum policy_mgr_con_mode type, + enum policy_mgr_conc_priority_mode sys_pref, uint8_t dbs_capable); +#else +static inline QDF_STATUS policy_mgr_incr_connection_count_utfw( + struct wlan_objmgr_psoc *psoc, uint32_t vdev_id, + uint32_t tx_streams, uint32_t rx_streams, + uint32_t chain_mask, uint32_t type, uint32_t sub_type, + uint32_t channelid, uint32_t mac_id) +{ + return QDF_STATUS_SUCCESS; +} +static inline QDF_STATUS policy_mgr_update_connection_info_utfw( + struct wlan_objmgr_psoc *psoc, uint32_t vdev_id, + uint32_t tx_streams, uint32_t rx_streams, + uint32_t chain_mask, uint32_t type, uint32_t sub_type, + uint32_t channelid, uint32_t mac_id) +{ + return QDF_STATUS_SUCCESS; +} +static inline QDF_STATUS policy_mgr_decr_connection_count_utfw( + struct wlan_objmgr_psoc *psoc, uint32_t del_all, + uint32_t vdev_id) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * policy_mgr_convert_device_mode_to_qdf_type() - provides the + * type translation from HDD to policy manager type + * @device_mode: Generic connection mode type + * + * + * This function provides the type translation + * + * Return: policy_mgr_con_mode enum + */ +enum policy_mgr_con_mode policy_mgr_convert_device_mode_to_qdf_type( + enum QDF_OPMODE device_mode); + +/** + * policy_mgr_get_qdf_mode_from_pm - provides the + * type translation from policy manager type + * to generic connection mode type + * @device_mode: policy manager mode type + * + * + * This function provides the type translation + * + * Return: QDF_OPMODE enum + */ +enum QDF_OPMODE policy_mgr_get_qdf_mode_from_pm( + enum policy_mgr_con_mode device_mode); + +/** + * policy_mgr_check_n_start_opportunistic_timer - check single mac upgrade + * needed or not, if needed start the oppurtunistic timer. + * @psoc: pointer to SOC + * + * This function starts the oppurtunistic timer if hw_mode change is needed + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_check_n_start_opportunistic_timer( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_pdev_set_hw_mode() - Set HW mode command to FW + * @psoc: PSOC object information + * @session_id: Session ID + * @mac0_ss: MAC0 spatial stream configuration + * @mac0_bw: MAC0 bandwidth configuration + * @mac1_ss: MAC1 spatial stream configuration + * @mac1_bw: MAC1 bandwidth configuration + * @mac0_band_cap: mac0 band capability requirement + * (0: Don't care, 1: 2.4G, 2: 5G) + * @dbs: HW DBS capability + * @dfs: HW Agile DFS capability + * @sbs: HW SBS capability + * @reason: Reason for connection update + * @next_action: next action to happen at policy mgr after + * HW mode change + * + * Sends the set hw mode request to FW + * + * e.g.: To configure 2x2_80 + * mac0_ss = HW_MODE_SS_2x2, mac0_bw = HW_MODE_80_MHZ + * mac1_ss = HW_MODE_SS_0x0, mac1_bw = HW_MODE_BW_NONE + * mac0_band_cap = HW_MODE_MAC_BAND_NONE, + * dbs = HW_MODE_DBS_NONE, dfs = HW_MODE_AGILE_DFS_NONE, + * sbs = HW_MODE_SBS_NONE + * e.g.: To configure 1x1_80_1x1_40 (DBS) + * mac0_ss = HW_MODE_SS_1x1, mac0_bw = HW_MODE_80_MHZ + * mac1_ss = HW_MODE_SS_1x1, mac1_bw = HW_MODE_40_MHZ + * mac0_band_cap = HW_MODE_MAC_BAND_NONE, + * dbs = HW_MODE_DBS, dfs = HW_MODE_AGILE_DFS_NONE, + * sbs = HW_MODE_SBS_NONE + * e.g.: To configure 1x1_80_1x1_40 (Agile DFS) + * mac0_ss = HW_MODE_SS_1x1, mac0_bw = HW_MODE_80_MHZ + * mac1_ss = HW_MODE_SS_1x1, mac1_bw = HW_MODE_40_MHZ + * mac0_band_cap = HW_MODE_MAC_BAND_NONE, + * dbs = HW_MODE_DBS, dfs = HW_MODE_AGILE_DFS, + * sbs = HW_MODE_SBS_NONE + * e.g.: To configure 2x2_5g_80+1x1_2g_40 + * mac0_ss = HW_MODE_SS_2x2, mac0_bw = HW_MODE_80_MHZ + * mac1_ss = HW_MODE_SS_1x1, mac1_bw = HW_MODE_40_MHZ + * mac0_band_cap = HW_MODE_MAC_BAND_5G + * dbs = HW_MODE_DBS, dfs = HW_MODE_AGILE_DFS_NONE, + * sbs = HW_MODE_SBS_NONE + * e.g.: To configure 2x2_2g_40+1x1_5g_40 + * mac0_ss = HW_MODE_SS_2x2, mac0_bw = HW_MODE_40_MHZ + * mac1_ss = HW_MODE_SS_1x1, mac1_bw = HW_MODE_40_MHZ + * mac0_band_cap = HW_MODE_MAC_BAND_2G + * dbs = HW_MODE_DBS, dfs = HW_MODE_AGILE_DFS_NONE, + * sbs = HW_MODE_SBS_NONE + * + * Return: Success if the message made it down to the next layer + */ +QDF_STATUS policy_mgr_pdev_set_hw_mode(struct wlan_objmgr_psoc *psoc, + uint32_t session_id, + enum hw_mode_ss_config mac0_ss, + enum hw_mode_bandwidth mac0_bw, + enum hw_mode_ss_config mac1_ss, + enum hw_mode_bandwidth mac1_bw, + enum hw_mode_mac_band_cap mac0_band_cap, + enum hw_mode_dbs_capab dbs, + enum hw_mode_agile_dfs_capab dfs, + enum hw_mode_sbs_capab sbs, + enum policy_mgr_conn_update_reason reason, + uint8_t next_action); + +/** + * policy_mgr_pdev_set_hw_mode_cback() - callback invoked by + * other component to provide set HW mode request status + * @status: status of the request + * @cfgd_hw_mode_index: new HW mode index + * @num_vdev_mac_entries: Number of mac entries + * @vdev_mac_map: The table of vdev to mac mapping + * @next_action: next action to happen at policy mgr after + * beacon update + * @reason: Reason for set HW mode + * @session_id: vdev id on which the request was made + * @context: PSOC object information + * + * This function is the callback registered with SME at set HW + * mode request time + * + * Return: None + */ +typedef void (*policy_mgr_pdev_set_hw_mode_cback)(uint32_t status, + uint32_t cfgd_hw_mode_index, + uint32_t num_vdev_mac_entries, + struct policy_mgr_vdev_mac_map *vdev_mac_map, + uint8_t next_action, + enum policy_mgr_conn_update_reason reason, + uint32_t session_id, void *context); + +/** + * policy_mgr_nss_update_cback() - callback invoked by other + * component to provide nss update request status + * @psoc: PSOC object information + * @tx_status: tx completion status for updated beacon with new + * nss value + * @vdev_id: vdev id for the specific connection + * @next_action: next action to happen at policy mgr after + * beacon update + * @reason: Reason for nss update + * @original_vdev_id: original request hwmode change vdev id + * + * This function is the callback registered with SME at nss + * update request time + * + * Return: None + */ +typedef void (*policy_mgr_nss_update_cback)(struct wlan_objmgr_psoc *psoc, + uint8_t tx_status, + uint8_t vdev_id, + uint8_t next_action, + enum policy_mgr_conn_update_reason reason, + uint32_t original_vdev_id); + +/** + * struct policy_mgr_sme_cbacks - SME Callbacks to be invoked + * from policy manager + * @sme_get_valid_channels: Get valid channel list + * @sme_get_nss_for_vdev: Get the allowed nss value for the vdev + * @sme_soc_set_dual_mac_config: Set the dual MAC scan & FW + * config + * @sme_pdev_set_hw_mode: Set the new HW mode to FW + * @sme_pdev_set_pcl: Set new PCL to FW + * @sme_nss_update_request: Update NSS value to FW + * @sme_change_mcc_beacon_interval: Set MCC beacon interval to FW + */ +struct policy_mgr_sme_cbacks { + QDF_STATUS (*sme_get_valid_channels)(uint8_t *chan_list, + uint32_t *list_len); + void (*sme_get_nss_for_vdev)(enum QDF_OPMODE, + uint8_t *nss_2g, uint8_t *nss_5g); + QDF_STATUS (*sme_soc_set_dual_mac_config)( + struct policy_mgr_dual_mac_config msg); + QDF_STATUS (*sme_pdev_set_hw_mode)(struct policy_mgr_hw_mode msg); + QDF_STATUS (*sme_pdev_set_pcl)(struct policy_mgr_pcl_list *msg); + QDF_STATUS (*sme_nss_update_request)(uint32_t vdev_id, + uint8_t new_nss, policy_mgr_nss_update_cback cback, + uint8_t next_action, struct wlan_objmgr_psoc *psoc, + enum policy_mgr_conn_update_reason reason, + uint32_t original_vdev_id); + QDF_STATUS (*sme_change_mcc_beacon_interval)(uint8_t session_id); + QDF_STATUS (*sme_get_ap_channel_from_scan)( + void *roam_profile, + void **scan_cache, + uint8_t *channel); + QDF_STATUS (*sme_scan_result_purge)( + void *scan_result); +}; + +/** + * struct policy_mgr_hdd_cbacks - HDD Callbacks to be invoked + * from policy manager + * @sap_restart_chan_switch_cb: Restart SAP + * @wlan_hdd_get_channel_for_sap_restart: Get channel to restart + * SAP + * @get_mode_for_non_connected_vdev: Get the mode for a non + * connected vdev + * @hdd_get_device_mode: Get QDF_OPMODE type for session id (vdev id) + * @hdd_wapi_security_sta_exist: Get whether wapi encription station existing + * or not. Some hw doesn't support WAPI encryption concurrency with other + * encryption type. + */ +struct policy_mgr_hdd_cbacks { + void (*sap_restart_chan_switch_cb)(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint32_t channel, + uint32_t channel_bw, + bool forced); + QDF_STATUS (*wlan_hdd_get_channel_for_sap_restart)( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint8_t *channel, + uint8_t *sec_ch); + enum policy_mgr_con_mode (*get_mode_for_non_connected_vdev)( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); + enum QDF_OPMODE (*hdd_get_device_mode)(uint32_t session_id); + bool (*hdd_wapi_security_sta_exist)(void); +}; + + +/** + * struct policy_mgr_tdls_cbacks - TDLS Callbacks to be invoked + * from policy manager + * @set_tdls_ct_mode: Set the tdls connection tracker mode + * @check_is_tdls_allowed: check if tdls allowed or not + */ +struct policy_mgr_tdls_cbacks { + void (*tdls_notify_increment_session)(struct wlan_objmgr_psoc *psoc); + void (*tdls_notify_decrement_session)(struct wlan_objmgr_psoc *psoc); +}; + +/** + * struct policy_mgr_cdp_cbacks - CDP Callbacks to be invoked + * from policy manager + * @cdp_update_mac_id: update mac_id for vdev + */ +struct policy_mgr_cdp_cbacks { + void (*cdp_update_mac_id)(struct wlan_objmgr_psoc *soc, + uint8_t vdev_id, uint8_t mac_id); +}; + +/** + * struct policy_mgr_dp_cbacks - CDP Callbacks to be invoked + * from policy manager + * @hdd_disable_rx_ol_in_concurrency: Callback to disable LRO/GRO offloads + * @hdd_set_rx_mode_rps_cb: Callback to set RPS + * @hdd_ipa_set_mcc_mode_cb: Callback to set mcc mode for ipa module + * @hdd_v2_flow_pool_map: Callback to create vdev flow pool + * @hdd_v2_flow_pool_unmap: Callback to delete vdev flow pool + */ +struct policy_mgr_dp_cbacks { + void (*hdd_disable_rx_ol_in_concurrency)(bool); + void (*hdd_set_rx_mode_rps_cb)(bool); + void (*hdd_ipa_set_mcc_mode_cb)(bool); + void (*hdd_v2_flow_pool_map)(int); + void (*hdd_v2_flow_pool_unmap)(int); +}; + +/** + * struct policy_mgr_wma_cbacks - WMA Callbacks to be invoked + * from policy manager + * @wma_get_connection_info: Get the connection related info + * from wma table + */ +struct policy_mgr_wma_cbacks { + QDF_STATUS (*wma_get_connection_info)(uint8_t vdev_id, + struct policy_mgr_vdev_entry_info *conn_table_entry); +}; + +/** +* policy_mgr_need_opportunistic_upgrade - check whether needs to change current +* HW mode to single mac 2x2 or the other DBS mode(for Dual DBS HW only). +* @psoc: PSOC object information +* @reason: enum policy_mgr_conn_update_reason +* +* This function is to check whether needs to change to single Mac mode. +* when opportunistic timer fired. But a special case for Dual DBS HW, this +* function will check DBS to DBS change is required or not: +* 1. For Dual DBS HW, if user set vdev priority list, we may need to do +* DBS to DBS switching. +* eg. P2P GO (2g) < SAP (5G) < STA (2g) in DBS2. +* If STA down, we need to switch to DBS1: P2P GO (2g) < SAP (5g). +* So, for opportunistic checking, we need to add DBS ->DBS checking +* as well. +* 2. Reason code : +* DBS -> Single MAC : POLICY_MGR_UPDATE_REASON_OPPORTUNISTIC +* DBS -> DBS : POLICY_MGR_UPDATE_REASON_PRI_VDEV_CHANGE +* +* return: PM_NOP, upgrade is not needed, otherwise new action type +* and reason code be returned. +*/ +enum policy_mgr_conc_next_action policy_mgr_need_opportunistic_upgrade( + struct wlan_objmgr_psoc *psoc, + enum policy_mgr_conn_update_reason *reason); + +/** + * policy_mgr_next_actions() - initiates actions needed on current + * connections once channel has been decided for the new + * connection + * @psoc: PSOC object information + * @session_id: Session id + * @action: action to be executed + * @reason: Reason for connection update + * + * This function initiates initiates actions + * needed on current connections once channel has been decided + * for the new connection. Notifies UMAC & FW as well + * + * Return: QDF_STATUS enum + */ +QDF_STATUS policy_mgr_next_actions(struct wlan_objmgr_psoc *psoc, + uint32_t session_id, + enum policy_mgr_conc_next_action action, + enum policy_mgr_conn_update_reason reason); + +/** + * policy_mgr_set_dual_mac_scan_config() - Set the dual MAC scan config + * @psoc: PSOC object information + * @dbs_val: Value of DBS bit + * @dbs_plus_agile_scan_val: Value of DBS plus agile scan bit + * @single_mac_scan_with_dbs_val: Value of Single MAC scan with DBS + * + * Set the values of scan config. For FW mode config, the existing values + * will be retained + * + * Return: None + */ +void policy_mgr_set_dual_mac_scan_config(struct wlan_objmgr_psoc *psoc, + uint8_t dbs_val, + uint8_t dbs_plus_agile_scan_val, + uint8_t single_mac_scan_with_dbs_val); + +/** + * policy_mgr_set_dual_mac_fw_mode_config() - Set the dual mac FW mode config + * @psoc: PSOC object information + * @dbs: DBS bit + * @dfs: Agile DFS bit + * + * Set the values of fw mode config. For scan config, the existing values + * will be retain. + * + * Return: None + */ +void policy_mgr_set_dual_mac_fw_mode_config(struct wlan_objmgr_psoc *psoc, + uint8_t dbs, uint8_t dfs); + +/** + * policy_mgr_soc_set_dual_mac_cfg_cb() - Callback for set dual mac config + * @status: Status of set dual mac config + * @scan_config: Current scan config whose status is the first param + * @fw_mode_config: Current FW mode config whose status is the first param + * + * Callback on setting the dual mac configuration + * + * Return: None + */ +void policy_mgr_soc_set_dual_mac_cfg_cb(enum set_hw_mode_status status, + uint32_t scan_config, uint32_t fw_mode_config); + +/** + * policy_mgr_map_concurrency_mode() - to map concurrency mode + * between sme and hdd + * @old_mode: sme provided adapter mode + * @new_mode: hdd provided concurrency mode + * + * This routine will map concurrency mode between sme and hdd + * + * Return: true or false + */ +bool policy_mgr_map_concurrency_mode(enum QDF_OPMODE *old_mode, + enum policy_mgr_con_mode *new_mode); + +/** + * policy_mgr_get_channel_from_scan_result() - to get channel from scan result + * @psoc: PSOC object information + * @roam_profile: pointer to roam profile + * @channel: channel to be filled + * + * This routine gets channel which most likely a candidate to which STA + * will make connection. + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_channel_from_scan_result( + struct wlan_objmgr_psoc *psoc, + void *roam_profile, uint8_t *channel); + +/** + * policy_mgr_mode_specific_num_open_sessions() - to get number of open sessions + * for a specific mode + * @psoc: PSOC object information + * @mode: device mode + * @num_sessions: to store num open sessions + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_mode_specific_num_open_sessions( + struct wlan_objmgr_psoc *psoc, enum QDF_OPMODE mode, + uint8_t *num_sessions); + +/** + * policy_mgr_mode_specific_num_active_sessions() - to get number of active + * sessions for a specific mode + * @psoc: PSOC object information + * @mode: device mode + * @num_sessions: to store num active sessions + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_mode_specific_num_active_sessions( + struct wlan_objmgr_psoc *psoc, enum QDF_OPMODE mode, + uint8_t *num_sessions); + +/** + * policy_mgr_concurrent_open_sessions_running() - Checks for + * concurrent open session + * @psoc: PSOC object information + * + * Checks if more than one open session is running for all the allowed modes + * in the driver + * + * Return: True if more than one open session exists, False otherwise + */ +bool policy_mgr_concurrent_open_sessions_running( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_max_concurrent_connections_reached() - Check if + * max conccurrency is reached + * @psoc: PSOC object information + * Checks for presence of concurrency where more than one connection exists + * + * Return: True if the max concurrency is reached, False otherwise + * + * Example: + * STA + STA (wlan0 and wlan1 are connected) - returns true + * STA + STA (wlan0 connected and wlan1 disconnected) - returns false + * DUT with P2P-GO + P2P-CLIENT connection) - returns true + * + */ +bool policy_mgr_max_concurrent_connections_reached( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_clear_concurrent_session_count() - Clear active session count + * @psoc: PSOC object information + * Clears the active session count for all modes + * + * Return: None + */ +void policy_mgr_clear_concurrent_session_count(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_multiple_active_sta_sessions() - Check for + * multiple STA connections + * @psoc: PSOC object information + * + * Checks if multiple active STA connection are in the driver + * + * Return: True if multiple STA sessions are present, False otherwise + * + */ +bool policy_mgr_is_multiple_active_sta_sessions( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_sta_active_connection_exists() - Check if a STA + * connection is active + * @psoc: PSOC object information + * + * Checks if there is atleast one active STA connection in the driver + * + * Return: True if an active STA session is present, False otherwise + */ +bool policy_mgr_is_sta_active_connection_exists( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_concurrent_beaconing_sessions_running() - Checks + * for concurrent beaconing entities + * @psoc: PSOC object information + * + * Checks if multiple beaconing sessions are running i.e., if SAP or GO or IBSS + * are beaconing together + * + * Return: True if multiple entities are beaconing together, False otherwise + */ +bool policy_mgr_concurrent_beaconing_sessions_running( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_wait_for_connection_update() - Wait for hw mode + * command to get processed + * @psoc: PSOC object information + * Waits for CONNECTION_UPDATE_TIMEOUT duration until the set hw mode + * response sets the event connection_update_done_evt + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_wait_for_connection_update( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_reset_connection_update() - Reset connection + * update event + * @psoc: PSOC object information + * Resets the concurrent connection update event + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_reset_connection_update(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_set_connection_update() - Set connection update + * event + * @psoc: PSOC object information + * Sets the concurrent connection update event + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_set_connection_update(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_set_chan_switch_complete_evt() - set channel + * switch completion event + * @psoc: PSOC object information + * Sets the channel switch completion event. + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_set_chan_switch_complete_evt( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_reset_chan_switch_complete_evt() - reset channel + * switch completion event + * @psoc: PSOC object information + * Resets the channel switch completion event. + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_reset_chan_switch_complete_evt( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_set_opportunistic_update() - Set opportunistic + * update event + * @psoc: PSOC object information + * Sets the opportunistic update event + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_set_opportunistic_update(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_stop_opportunistic_timer() - Stops opportunistic timer + * @psoc: PSOC object information + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_stop_opportunistic_timer(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_restart_opportunistic_timer() - Restarts opportunistic timer + * @psoc: PSOC object information + * @check_state: check timer state if this flag is set, else restart + * irrespective of state + * + * Restarts opportunistic timer for DBS_OPPORTUNISTIC_TIME seconds. + * Check if current state is RUNNING if check_state is set, else + * restart the timer irrespective of state. + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_restart_opportunistic_timer( + struct wlan_objmgr_psoc *psoc, bool check_state); + +/** + * policy_mgr_modify_sap_pcl_based_on_mandatory_channel() - + * Modify SAPs PCL based on mandatory channel list + * @psoc: PSOC object information + * @pcl_list_org: Pointer to the preferred channel list to be trimmed + * @weight_list_org: Pointer to the weights of the preferred channel list + * @pcl_len_org: Pointer to the length of the preferred chanel list + * + * Modifies the preferred channel list of SAP based on the mandatory channel + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_modify_sap_pcl_based_on_mandatory_channel( + struct wlan_objmgr_psoc *psoc, uint8_t *pcl_list_org, + uint8_t *weight_list_org, uint32_t *pcl_len_org); + +/** + * policy_mgr_update_and_wait_for_connection_update() - Update and wait for + * connection update + * @psoc: PSOC object information + * @session_id: Session id + * @channel: Channel number + * @reason: Reason for connection update + * + * Update the connection to either single MAC or dual MAC and wait for the + * update to complete + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_update_and_wait_for_connection_update( + struct wlan_objmgr_psoc *psoc, uint8_t session_id, + uint8_t channel, enum policy_mgr_conn_update_reason reason); + +/** + * policy_mgr_is_sap_mandatory_channel_set() - Checks if SAP + * mandatory channel is set + * @psoc: PSOC object information + * Checks if any mandatory channel is set for SAP operation + * + * Return: True if mandatory channel is set, false otherwise + */ +bool policy_mgr_is_sap_mandatory_channel_set(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_list_has_24GHz_channel() - Check if list contains 2.4GHz channels + * @channel_list: Channel list + * @list_len: Length of the channel list + * + * Checks if the channel list contains atleast one 2.4GHz channel + * + * Return: True if 2.4GHz channel is present, false otherwise + */ +bool policy_mgr_list_has_24GHz_channel(uint8_t *channel_list, + uint32_t list_len); + +/** + * policy_mgr_get_valid_chans() - Get the valid channel list + * @psoc: PSOC object information + * @chan_list: Pointer to the valid channel list + * @list_len: Pointer to the length of the valid channel list + * + * Gets the valid channel list filtered by band + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_valid_chans(struct wlan_objmgr_psoc *psoc, + uint8_t *chan_list, uint32_t *list_len); + +/** + * policy_mgr_get_nss_for_vdev() - Get the allowed nss value for the + * vdev + * @psoc: PSOC object information + * @dev_mode: connection type. + * @nss2g: Pointer to the 2G Nss parameter. + * @nss5g: Pointer to the 5G Nss parameter. + * + * Fills the 2G and 5G Nss values based on connection type. + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_nss_for_vdev(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint8_t *nss_2g, uint8_t *nss_5g); + +/** + * policy_mgr_get_sap_mandatory_channel() - Get the mandatory channel for SAP + * @psoc: PSOC object information + * @chan: Pointer to the SAP mandatory channel + * + * Gets the mandatory channel for SAP operation + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_sap_mandatory_channel(struct wlan_objmgr_psoc *psoc, + uint32_t *chan); + +/** + * policy_mgr_set_sap_mandatory_channels() - Set the mandatory channel for SAP + * @psoc: PSOC object information + * @channels: Channel list to be set + * @len: Length of the channel list + * + * Sets the channels for the mandatory channel list along with the length of + * of the channel list. + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_set_sap_mandatory_channels(struct wlan_objmgr_psoc *psoc, + uint8_t *channels, uint32_t len); + +/** + * policy_mgr_is_any_mode_active_on_band_along_with_session() - + * Check if any connection mode is active on a band along with + * the given session + * @psoc: PSOC object information + * @session_id: Session along which active sessions are looked for + * @band: Operating frequency band of the connection + * POLICY_MGR_BAND_24: Looks for active connection on 2.4 GHz only + * POLICY_MGR_BAND_5: Looks for active connection on 5 GHz only + * + * Checks if any of the connection mode is active on a given frequency band + * + * Return: True if any connection is active on a given band, false otherwise + */ +bool policy_mgr_is_any_mode_active_on_band_along_with_session( + struct wlan_objmgr_psoc *psoc, uint8_t session_id, + enum policy_mgr_band band); + +/** + * policy_mgr_get_chan_by_session_id() - Get channel for a given session ID + * @psoc: PSOC object information + * @session_id: Session ID + * @chan: Pointer to the channel + * + * Gets the channel for a given session ID + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_chan_by_session_id(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, uint8_t *chan); + +/** + * policy_mgr_get_mac_id_by_session_id() - Get MAC ID for a given session ID + * @psoc: PSOC object information + * @session_id: Session ID + * @mac_id: Pointer to the MAC ID + * + * Gets the MAC ID for a given session ID + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_mac_id_by_session_id(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, uint8_t *mac_id); + +/** + * policy_mgr_get_mcc_session_id_on_mac() - Get MCC session's ID + * @psoc: PSOC object information + * @mac_id: MAC ID on which MCC session needs to be found + * @session_id: Session with which MCC combination needs to be found + * @mcc_session_id: Pointer to the MCC session ID + * + * Get the session ID of the MCC interface + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_mcc_session_id_on_mac(struct wlan_objmgr_psoc *psoc, + uint8_t mac_id, uint8_t session_id, + uint8_t *mcc_session_id); + +/** + * policy_mgr_get_mcc_operating_channel() - Get the MCC channel + * @psoc: PSOC object information + * @session_id: Session ID with which MCC is being done + * + * Gets the MCC channel for a given session ID. + * + * Return: '0' (INVALID_CHANNEL_ID) or valid channel number + */ +uint8_t policy_mgr_get_mcc_operating_channel(struct wlan_objmgr_psoc *psoc, + uint8_t session_id); + +/** + * policy_mgr_get_pcl_for_existing_conn() - Get PCL for existing connection + * @psoc: PSOC object information + * @mode: Connection mode of type 'policy_mgr_con_mode' + * @pcl_ch: Pointer to the PCL + * @len: Pointer to the length of the PCL + * @pcl_weight: Pointer to the weights of the PCL + * @weight_len: Max length of the weights list + * @all_matching_cxn_to_del: Need remove all entries before getting pcl + * + * Get the PCL for an existing connection + * + * Return: None + */ +QDF_STATUS policy_mgr_get_pcl_for_existing_conn(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint8_t *pcl_ch, uint32_t *len, + uint8_t *weight_list, uint32_t weight_len, + bool all_matching_cxn_to_del); + +/** + * policy_mgr_get_valid_chan_weights() - Get the weightage for + * all valid channels + * @psoc: PSOC object information + * @weight: Pointer to the structure containing pcl, saved channel list and + * weighed channel list + * + * Provides the weightage for all valid channels. This compares the PCL list + * with the valid channel list. The channels present in the PCL get their + * corresponding weightage and the non-PCL channels get the default weightage + * of WEIGHT_OF_NON_PCL_CHANNELS. + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_valid_chan_weights(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_pcl_chan_weights *weight); + +/** + * policy_mgr_set_hw_mode_on_channel_switch() - Set hw mode + * after channel switch + * @session_id: Session ID + * + * Sets hw mode after doing a channel switch + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_set_hw_mode_on_channel_switch( + struct wlan_objmgr_psoc *psoc, uint8_t session_id); + +/** + * policy_mgr_set_do_hw_mode_change_flag() - Set flag to indicate hw mode change + * @psoc: PSOC object information + * @flag: Indicate if hw mode change is required or not + * + * Set the flag to indicate whether a hw mode change is required after a + * vdev up or not. Flag value of true indicates that a hw mode change is + * required after vdev up. + * + * Return: None + */ +void policy_mgr_set_do_hw_mode_change_flag(struct wlan_objmgr_psoc *psoc, + bool flag); + +/** + * policy_mgr_is_hw_mode_change_after_vdev_up() - Check if hw + * mode change is needed + * @psoc: PSOC object information + * Returns the flag which indicates if a hw mode change is required after + * vdev up. + * + * Return: True if hw mode change is required, false otherwise + */ +bool policy_mgr_is_hw_mode_change_after_vdev_up(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_checkn_update_hw_mode_single_mac_mode() - Set hw_mode to SMM + * if required + * @psoc: PSOC object information + * @channel: channel number for the new STA connection + * + * After the STA disconnection, if the hw_mode is in DBS and the new STA + * connection is coming in the band in which existing connections are + * present, then this function stops the dbs opportunistic timer and sets + * the hw_mode to Single MAC mode (SMM). + * + * Return: None + */ +void policy_mgr_checkn_update_hw_mode_single_mac_mode( + struct wlan_objmgr_psoc *psoc, uint8_t channel); + +/** + * policy_mgr_dump_connection_status_info() - Dump the concurrency information + * @psoc: PSOC object information + * Prints the concurrency information such as tx/rx spatial stream, chainmask, + * etc. + * + * Return: None + */ +void policy_mgr_dump_connection_status_info(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_mode_specific_vdev_id() - provides the + * vdev id of the pecific mode + * @psoc: PSOC object information + * @mode: type of connection + * + * This function provides vdev id for the given mode + * + * Return: vdev id + */ +uint32_t policy_mgr_mode_specific_vdev_id(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode); + +/** + * policy_mgr_mode_specific_connection_count() - provides the + * count of connections of specific mode + * @psoc: PSOC object information + * @mode: type of connection + * @list: To provide the indices on pm_conc_connection_list + * (optional) + * + * This function provides the count of current connections + * + * Return: connection count of specific type + */ +uint32_t policy_mgr_mode_specific_connection_count( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode, + uint32_t *list); + +/** + * policy_mgr_check_conn_with_mode_and_vdev_id() - checks if any active + * session with specific mode and vdev_id + * @psoc: PSOC object information + * @mode: type of connection + * @vdev_id: vdev_id of the connection + * + * This function checks if any active session with specific mode and vdev_id + * is present + * + * Return: QDF STATUS with success if active session is found, else failure + */ +QDF_STATUS policy_mgr_check_conn_with_mode_and_vdev_id( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode, + uint32_t vdev_id); + +/** + * policy_mgr_hw_mode_transition_cb() - Callback for HW mode + * transition from FW + * @old_hw_mode_index: Old HW mode index + * @new_hw_mode_index: New HW mode index + * @num_vdev_mac_entries: Number of vdev-mac id mapping that follows + * @vdev_mac_map: vdev-mac id map. This memory will be freed by the caller. + * So, make local copy if needed. + * + * Provides the old and new HW mode index set by the FW + * + * Return: None + */ +void policy_mgr_hw_mode_transition_cb(uint32_t old_hw_mode_index, + uint32_t new_hw_mode_index, + uint32_t num_vdev_mac_entries, + struct policy_mgr_vdev_mac_map *vdev_mac_map, + struct wlan_objmgr_psoc *context); + +/** + * policy_mgr_current_concurrency_is_mcc() - To check the current + * concurrency combination if it is doing MCC + * @psoc: PSOC object information + * This routine is called to check if it is doing MCC + * + * Return: True - MCC, False - Otherwise + */ +bool policy_mgr_current_concurrency_is_mcc(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_register_sme_cb() - register SME callbacks + * @psoc: PSOC object information + * @sme_cbacks: function pointers from SME + * + * API, allows SME to register callbacks to be invoked by policy + * mgr + * + * Return: SUCCESS, + * Failure (if registration fails) + */ +QDF_STATUS policy_mgr_register_sme_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_sme_cbacks *sme_cbacks); + +/** + * policy_mgr_register_hdd_cb() - register HDD callbacks + * @psoc: PSOC object information + * @hdd_cbacks: function pointers from HDD + * + * API, allows HDD to register callbacks to be invoked by policy + * mgr + * + * Return: SUCCESS, + * Failure (if registration fails) + */ +QDF_STATUS policy_mgr_register_hdd_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_hdd_cbacks *hdd_cbacks); + +/** + * policy_mgr_deregister_hdd_cb() - Deregister HDD callbacks + * @psoc: PSOC object information + * + * API, allows HDD to deregister callbacks + * + * Return: SUCCESS, + * Failure (if de-registration fails) + */ +QDF_STATUS policy_mgr_deregister_hdd_cb(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_register_tdls_cb() - register TDLS callbacks + * @psoc: PSOC object information + * @tdls_cbacks: function pointers from TDLS + * + * API, allows TDLS to register callbacks to be invoked by + * policy mgr + * + * Return: SUCCESS, + * Failure (if registration fails) + */ +QDF_STATUS policy_mgr_register_tdls_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_tdls_cbacks *tdls_cbacks); + +/** + * policy_mgr_register_cdp_cb() - register CDP callbacks + * @psoc: PSOC object information + * @cdp_cbacks: function pointers from CDP + * + * API, allows CDP to register callbacks to be invoked by + * policy mgr + * + * Return: SUCCESS, + * Failure (if registration fails) + */ +QDF_STATUS policy_mgr_register_cdp_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_cdp_cbacks *cdp_cbacks); + +/** + * policy_mgr_register_dp_cb() - register CDP callbacks + * @psoc: PSOC object information + * @cdp_cbacks: function pointers from CDP + * + * API, allows CDP to register callbacks to be invoked by + * policy mgr + * + * Return: SUCCESS, + * Failure (if registration fails) + */ +QDF_STATUS policy_mgr_register_dp_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_dp_cbacks *dp_cbacks); + +/** + * policy_mgr_register_wma_cb() - register WMA callbacks + * @psoc: PSOC object information + * @wma_cbacks: function pointers from WMA + * + * API, allows WMA to register callbacks to be invoked by policy + * mgr + * + * Return: SUCCESS, + * Failure (if registration fails) + */ +QDF_STATUS policy_mgr_register_wma_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_wma_cbacks *wma_cbacks); + +/** + * policy_mgr_is_dbs_enable() - Check if master DBS control is enabled + * @psoc: PSOC object information + * Checks if the master DBS control is enabled. This will be used + * to override any other DBS capability + * + * Return: True if master DBS control is enabled + */ +bool policy_mgr_is_dbs_enable(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_hw_dbs_capable() - Check if HW is DBS capable + * @psoc: PSOC object information + * Checks if the HW is DBS capable + * + * Return: true if the HW is DBS capable + */ +bool policy_mgr_is_hw_dbs_capable(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_hw_sbs_capable() - Check if HW is SBS capable + * @psoc: PSOC object information + * Checks if the HW is SBS capable + * + * Return: true if the HW is SBS capable + */ +bool policy_mgr_is_hw_sbs_capable(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_current_hwmode_dbs() - Check if current hw mode is DBS + * @psoc: PSOC object information + * Checks if current hardware mode of the system is DBS or no + * + * Return: true or false + */ +bool policy_mgr_is_current_hwmode_dbs(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_is_hw_dbs_2x2_capable() - if hardware is capable of dbs 2x2 + * @psoc: PSOC object information + * This function checks if hw_modes supported are always capable of + * DBS and there is no need for downgrading while entering DBS. + * true: DBS 2x2 can always be supported + * false: hw_modes support DBS 1x1 as well + * Genoa DBS 2x2 + 1x1 will not be included. + * + * Return: true - DBS2x2, false - DBS1x1 + */ +bool policy_mgr_is_hw_dbs_2x2_capable(struct wlan_objmgr_psoc *psoc); + +/* + * policy_mgr_is_2x2_1x1_dbs_capable() - check 2x2+1x1 DBS supported or not + * @psoc: PSOC object data + * + * This routine is called to check 2x2 5G + 1x1 2G (DBS1) or + * 2x2 2G + 1x1 5G (DBS2) support or not. + * Either DBS1 or DBS2 supported + * + * Return: true/false + */ +bool policy_mgr_is_2x2_1x1_dbs_capable(struct wlan_objmgr_psoc *psoc); + +/* + * policy_mgr_is_2x2_5G_1x1_2G_dbs_capable() - check Genoa DBS1 enabled or not + * @psoc: PSOC object data + * + * This routine is called to check support DBS1 or not. + * Notes: DBS1: 2x2 5G + 1x1 2G. + * This function will call policy_mgr_get_hw_mode_idx_from_dbs_hw_list to match + * the HW mode from hw mode list. The parameters will also be matched to + * 2x2 5G +2x2 2G HW mode. But firmware will not report 2x2 5G + 2x2 2G alone + * with 2x2 5G + 1x1 2G at same time. So, it is safe to find DBS1 with + * policy_mgr_get_hw_mode_idx_from_dbs_hw_list. + * + * Return: true/false + */ +bool policy_mgr_is_2x2_5G_1x1_2G_dbs_capable(struct wlan_objmgr_psoc *psoc); + +/* + * policy_mgr_is_2x2_2G_1x1_5G_dbs_capable() - check Genoa DBS2 enabled or not + * @psoc: PSOC object data + * + * This routine is called to check support DBS2 or not. + * Notes: DBS2: 2x2 2G + 1x1 5G + * + * Return: true/false + */ +bool policy_mgr_is_2x2_2G_1x1_5G_dbs_capable(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_init() - Policy Manager component initialization + * routine + * + * Return - QDF Status + */ +QDF_STATUS policy_mgr_init(void); + +/** + * policy_mgr_deinit() - Policy Manager component + * de-initialization routine + * + * Return - QDF Status + */ +QDF_STATUS policy_mgr_deinit(void); + +/** + * policy_mgr_psoc_enable() - Policy Manager component + * enable routine + * @psoc: PSOC object information + * + * Return - QDF Status + */ +QDF_STATUS policy_mgr_psoc_enable(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_psoc_disable() - Policy Manager component + * disable routine + * @psoc: PSOC object information + * + * Return - QDF Status + */ +QDF_STATUS policy_mgr_psoc_disable(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_psoc_open() - Policy Manager component + * open routine + * @psoc: PSOC object information + * + * Return - QDF Status + */ +QDF_STATUS policy_mgr_psoc_open(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_psoc_close() - Policy Manager component + * close routine + * @psoc: PSOC object information + * + * Return - QDF Status + */ +QDF_STATUS policy_mgr_psoc_close(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_num_dbs_hw_modes() - Get number of HW mode + * @psoc: PSOC object information + * Fetches the number of DBS HW modes returned by the FW + * + * Return: Negative value on error or returns the number of DBS HW modes + */ +int8_t policy_mgr_get_num_dbs_hw_modes(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_dbs_hw_modes() - Get the DBS HW modes for userspace + * @psoc: PSOC object information + * @one_by_one_dbs: 1x1 DBS capability of HW + * @two_by_two_dbs: 2x2 DBS capability of HW + * + * Provides the DBS HW mode capability such as whether + * 1x1 DBS, 2x2 DBS is supported by the HW or not. + * + * Return: Failure in case of error and 0 on success + * one_by_one_dbs/two_by_two_dbs will be false, + * if they are not supported. + * one_by_one_dbs/two_by_two_dbs will be true, + * if they are supported. + * false values of one_by_one_dbs/two_by_two_dbs, + * indicate DBS is disabled + */ +QDF_STATUS policy_mgr_get_dbs_hw_modes(struct wlan_objmgr_psoc *psoc, + bool *one_by_one_dbs, bool *two_by_two_dbs); + +/** + * policy_mgr_check_sta_ap_concurrent_ch_intf() - Restart SAP in STA-AP case + * @data: Pointer to STA adapter + * + * Restarts the SAP interface in STA-AP concurrency scenario + * + * Restart: None + */ +void policy_mgr_check_sta_ap_concurrent_ch_intf(void *data); + +/** + * policy_mgr_get_current_hw_mode() - Get current HW mode params + * @psoc: PSOC object information + * @hw_mode: HW mode parameters + * + * Provides the current HW mode parameters if the HW mode is initialized + * in the driver + * + * Return: Success if the current HW mode params are successfully populated + */ +QDF_STATUS policy_mgr_get_current_hw_mode(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_hw_mode_params *hw_mode); + +/** + * policy_mgr_get_dbs_plus_agile_scan_config() - Get DBS plus agile scan bit + * @psoc: PSOC object information + * Gets the DBS plus agile scan bit of concurrent_scan_config_bits + * + * Return: 0 or 1 to indicate the DBS plus agile scan bit + */ +bool policy_mgr_get_dbs_plus_agile_scan_config(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_single_mac_scan_with_dfs_config() - Get Single + * MAC scan with DFS bit + * @psoc: PSOC object information + * Gets the Single MAC scan with DFS bit of concurrent_scan_config_bits + * + * Return: 0 or 1 to indicate the Single MAC scan with DFS bit + */ +bool policy_mgr_get_single_mac_scan_with_dfs_config( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_set_hw_mode_change_in_progress() - Set value + * corresponding to policy_mgr_hw_mode_change that indicate if + * HW mode change is in progress + * @psoc: PSOC object information + * @value: Indicate if hw mode change is in progress + * + * Set the value corresponding to policy_mgr_hw_mode_change that + * indicated if hw mode change is in progress. + * + * Return: None + */ +void policy_mgr_set_hw_mode_change_in_progress( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_hw_mode_change value); + +/** + * policy_mgr_is_hw_mode_change_in_progress() - Check if HW mode + * change is in progress. + * @psoc: PSOC object information + * + * Returns the corresponding policy_mgr_hw_mode_change value. + * + * Return: policy_mgr_hw_mode_change value. + */ +enum policy_mgr_hw_mode_change policy_mgr_is_hw_mode_change_in_progress( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_hw_mode_change_from_hw_mode_index() - Get + * matching HW mode from index + * @psoc: PSOC object information + * @hw_mode_index: HW mode index + * Returns the corresponding policy_mgr_hw_mode_change HW mode. + * + * Return: policy_mgr_hw_mode_change value. + */ +enum policy_mgr_hw_mode_change policy_mgr_get_hw_mode_change_from_hw_mode_index( + struct wlan_objmgr_psoc *psoc, uint32_t hw_mode_index); + +/** + * policy_mgr_is_scan_simultaneous_capable() - Check if scan + * parallelization is supported or not + * @psoc: PSOC object information + * currently scan parallelization feature support is dependent on DBS but + * it can be independent in future. + * + * Return: True if master DBS control is enabled + */ +bool policy_mgr_is_scan_simultaneous_capable(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_set_user_cfg() - Function to set user cfg variables + * required by policy manager component + * @psoc: PSOC object information + * @user_cfg: User config valiables structure pointer + * + * This function sets the user cfg variables required by policy + * manager + * + * Return: SUCCESS or FAILURE + * + */ +QDF_STATUS policy_mgr_set_user_cfg(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_user_cfg *user_cfg); + +/** + * policy_mgr_init_dbs_config() - Function to initialize DBS + * config in policy manager component + * @psoc: PSOC object information + * @scan_config: DBS scan config + * @fw_config: DBS FW config + * + * This function sets the DBS configurations required by policy + * manager + * + * Return: SUCCESS or FAILURE + * + */ +void policy_mgr_init_dbs_config(struct wlan_objmgr_psoc *psoc, + uint32_t scan_config, uint32_t fw_config); + +/** + * policy_mgr_update_dbs_scan_config() - Function to update + * DBS scan config in policy manager component + * @psoc: PSOC object information + * + * This function updates the DBS scan configurations required by + * policy manager + * + * Return: SUCCESS or FAILURE + * + */ +void policy_mgr_update_dbs_scan_config(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_update_dbs_fw_config() - Function to update DBS FW + * config in policy manager component + * @psoc: PSOC object information + * + * This function updates the DBS FW configurations required by + * policy manager + * + * Return: SUCCESS or FAILURE + * + */ +void policy_mgr_update_dbs_fw_config(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_update_dbs_req_config() - Function to update DBS + * request config in policy manager component + * @psoc: PSOC object information + * @scan_config: DBS scan config + * @fw_config: DBS FW config + * + * This function updates DBS request configurations required by + * policy manager + * + * Return: SUCCESS or FAILURE + * + */ +void policy_mgr_update_dbs_req_config(struct wlan_objmgr_psoc *psoc, + uint32_t scan_config, uint32_t fw_mode_config); + +/** + * policy_mgr_dump_dbs_hw_mode() - Function to dump DBS config + * @psoc: PSOC object information + * + * This function dumps the DBS configurations + * + * Return: SUCCESS or FAILURE + * + */ +void policy_mgr_dump_dbs_hw_mode(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_init_dbs_hw_mode() - Function to initialize DBS HW + * modes in policy manager component + * @psoc: PSOC object information + * @num_dbs_hw_modes: Number of HW modes + * @ev_wlan_dbs_hw_mode_list: HW list + * + * This function to initialize the DBS HW modes in policy + * manager + * + * Return: SUCCESS or FAILURE + * + */ +void policy_mgr_init_dbs_hw_mode(struct wlan_objmgr_psoc *psoc, + uint32_t num_dbs_hw_modes, + uint32_t *ev_wlan_dbs_hw_mode_list); + +/** + * policy_mgr_update_hw_mode_list() - Function to initialize DBS + * HW modes in policy manager component + * @psoc: PSOC object information + * @tgt_hdl: Target psoc information + * + * This function to initialize the DBS HW modes in policy + * manager + * + * Return: SUCCESS or FAILURE + * + */ +QDF_STATUS policy_mgr_update_hw_mode_list(struct wlan_objmgr_psoc *psoc, + struct target_psoc_info *tgt_hdl); + +/** + * policy_mgr_update_hw_mode_index() - Function to update + * current HW mode in policy manager component + * @psoc: PSOC object information + * @new_hw_mode_index: index to new HW mode + * + * This function to update the current HW mode in policy manager + * + * Return: SUCCESS or FAILURE + * + */ +void policy_mgr_update_hw_mode_index(struct wlan_objmgr_psoc *psoc, + uint32_t new_hw_mode_index); + +/** + * policy_mgr_update_old_hw_mode_index() - Function to update + * old HW mode in policy manager component + * @psoc: PSOC object information + * @new_hw_mode_index: index to old HW mode + * + * This function to update the old HW mode in policy manager + * + * Return: SUCCESS or FAILURE + * + */ +void policy_mgr_update_old_hw_mode_index(struct wlan_objmgr_psoc *psoc, + uint32_t old_hw_mode_index); + +/** + * policy_mgr_update_new_hw_mode_index() - Function to update + * new HW mode in policy manager component + * @psoc: PSOC object information + * @new_hw_mode_index: index to new HW mode + * + * This function to update the new HW mode in policy manager + * + * Return: SUCCESS or FAILURE + * + */ +void policy_mgr_update_new_hw_mode_index(struct wlan_objmgr_psoc *psoc, + uint32_t new_hw_mode_index); + +/** + * policy_mgr_is_chan_ok_for_dnbs() - Function to check if a channel + * is OK for "Do Not Break Stream" + * @psoc: PSOC object information + * @channel: Channel to check. + * @ok: Pointer to flag in which status will be stored + * This function checks if a channel is OK for + * "Do Not Break Stream" + * Return: SUCCESS or FAILURE + */ +QDF_STATUS policy_mgr_is_chan_ok_for_dnbs(struct wlan_objmgr_psoc *psoc, + uint8_t channel, bool *ok); + +/** + * policy_mgr_get_hw_dbs_nss() - Computes DBS NSS + * @psoc: PSOC object information + * @nss_dbs: NSS info of both MAC0 and MAC1 + * This function computes NSS info of both MAC0 and MAC1 + * + * Return: uint32_t value signifies supported RF chains + */ +uint32_t policy_mgr_get_hw_dbs_nss(struct wlan_objmgr_psoc *psoc, + struct dbs_nss *nss_dbs); + +/** + * policy_mgr_is_dnsc_set - Check if user has set + * "Do_Not_Switch_Channel" for the vdev passed + * @vdev: vdev pointer + * + * Get "Do_Not_Switch_Channel" setting for the vdev passed. + * + * Return: true for success, else false + */ +bool policy_mgr_is_dnsc_set(struct wlan_objmgr_vdev *vdev); + +/** + * policy_mgr_get_updated_scan_and_fw_mode_config() - Function + * to get latest scan & fw config for DBS + * @psoc: PSOC object information + * @scan_config: DBS related scan config + * @fw_mode_config: DBS related FW config + * @dual_mac_disable_ini: DBS related ini config + * This function returns the latest DBS configuration for + * connection & scan, sent to FW + * Return: SUCCESS or FAILURE + */ +QDF_STATUS policy_mgr_get_updated_scan_and_fw_mode_config( + struct wlan_objmgr_psoc *psoc, uint32_t *scan_config, + uint32_t *fw_mode_config, uint32_t dual_mac_disable_ini, + uint32_t channel_select_logic_conc); + +/** + * policy_mgr_is_safe_channel - Check if the channel is in LTE + * coex channel avoidance list + * @psoc: PSOC object information + * @channel: channel to be checked + * + * Check if the channel is in LTE coex channel avoidance list. + * + * Return: true for success, else false + */ +bool policy_mgr_is_safe_channel(struct wlan_objmgr_psoc *psoc, + uint8_t channel); + +/** + * policy_mgr_is_force_scc() - checks if SCC needs to be + * mandated + * @psoc: PSOC object information + * + * This function checks if SCC needs to be mandated or not + * + * Return: True if SCC to be mandated, false otherwise + */ +bool policy_mgr_is_force_scc(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_valid_sap_conc_channel_check() - checks & updates + * the channel SAP to come up on in case of STA+SAP concurrency + * @psoc: PSOC object information + * @con_ch: pointer to the channel on which sap will come up + * @sap_ch: initial channel for SAP + * + * This function checks & updates the channel SAP to come up on in + * case of STA+SAP concurrency + * Return: Success if SAP can come up on a channel + */ +QDF_STATUS policy_mgr_valid_sap_conc_channel_check( + struct wlan_objmgr_psoc *psoc, uint8_t *con_ch, uint8_t sap_ch); + +/** + * policy_mgr_get_alternate_channel_for_sap() - Get an alternate + * channel to move the SAP to + * @psoc: PSOC object information + * + * This function returns an alternate channel for SAP to move to + * Return: The new channel for SAP + */ +uint8_t policy_mgr_get_alternate_channel_for_sap( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_disallow_mcc() - Check for mcc + * + * @psoc: PSOC object information + * @channel: channel on which new connection is coming up + * + * When a new connection is about to come up check if current + * concurrency combination including the new connection is + * causing MCC + * + * Return: True if it is causing MCC + */ +bool policy_mgr_disallow_mcc(struct wlan_objmgr_psoc *psoc, + uint8_t channel); + +/** + * policy_mgr_mode_specific_get_channel() - Get channel for a + * connection type + * @psoc: PSOC object information + * @chan_list: Connection type + * + * Get channel for a connection type + * + * Return: channel number + */ +uint8_t policy_mgr_mode_specific_get_channel( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode); + +/** + * policy_mgr_enable_disable_sap_mandatory_chan_list() - Update the value of + * enable_sap_mandatory_chan_list + * @psoc: Pointer to soc + * @val: value of enable_sap_mandatory_chan_list + * + * Update the value of enable_sap_mandatory_chan_list + * + * Return: void + */ +void policy_mgr_enable_disable_sap_mandatory_chan_list( + struct wlan_objmgr_psoc *psoc, bool val); + +/** + * policy_mgr_add_sap_mandatory_chan() - Add chan to SAP mandatory channel + * list + * @psoc: Pointer to soc + * @chan: Channel to be added + * + * Add chan to SAP mandatory channel list + * + * Return: None + */ +void policy_mgr_add_sap_mandatory_chan(struct wlan_objmgr_psoc *psoc, + uint8_t chan); + +/** + * policy_mgr_is_sap_mandatory_chan_list_enabled() - Return the SAP mandatory + * channel list enabled status + * @psoc: Pointer to soc + * + * Get the SAP mandatory channel list enabled status + * + * Return: Enable or Disable + */ +bool policy_mgr_is_sap_mandatory_chan_list_enabled( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_sap_mandatory_chan_list_len() - Return the SAP mandatory + * channel list len + * @psoc: Pointer to soc + * + * Get the SAP mandatory channel list len + * + * Return: Channel list length + */ +uint32_t policy_mgr_get_sap_mandatory_chan_list_len( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_init_sap_mandatory_2g_chan() - Init 2.4G SAP mandatory channel + * list + * @psoc: Pointer to soc + * + * Initialize the 2.4G SAP mandatory channels + * + * Return: None + */ +void policy_mgr_init_sap_mandatory_2g_chan(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_remove_sap_mandatory_chan() - Remove channel from SAP mandatory + * channel list + * @psoc: Pointer to soc + * @chan: channel to be removed from mandatory channel list + * + * Remove channel from SAP mandatory channel list + * + * Return: None + */ +void policy_mgr_remove_sap_mandatory_chan(struct wlan_objmgr_psoc *psoc, + uint8_t chan); +/* + * policy_set_cur_conc_system_pref - set current conc_system_pref + * @psoc: soc pointer + * + * Set the current concurrency system preference. + * + * Return: None + */ +void policy_mgr_set_cur_conc_system_pref(struct wlan_objmgr_psoc *psoc, + uint8_t conc_system_pref); +/** + * policy_mgr_get_cur_conc_system_pref - Get current conc_system_pref + * @psoc: soc pointer + * + * Get the current concurrent system preference. + * + * Return: conc_system_pref + */ +uint8_t policy_mgr_get_cur_conc_system_pref(struct wlan_objmgr_psoc *psoc); +/** + * policy_mgr_check_and_stop_opportunistic_timer - Get current + * state of opportunistic timer, if running, stop it and take + * action + * @psoc: soc pointer + * @id: Session/vdev id + * + * Get the current state of opportunistic timer, if it is + * running, stop it and take action. + * + * Return: None + */ +void policy_mgr_check_and_stop_opportunistic_timer( + struct wlan_objmgr_psoc *psoc, uint8_t id); + +/** + * policy_mgr_set_weight_of_dfs_passive_channels_to_zero() - set weight of dfs + * and passive channels to 0 + * @psoc: pointer to soc + * @pcl_channels: preferred channel list + * @len: length of preferred channel list + * @weight_list: preferred channel weight list + * @weight_len: length of weight list + * This function set the weight of dfs and passive channels to 0 + * + * Return: None + */ +void policy_mgr_set_weight_of_dfs_passive_channels_to_zero( + struct wlan_objmgr_psoc *psoc, uint8_t *pcl_channels, + uint32_t *len, uint8_t *weight_list, uint32_t weight_len); + +/** + * policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan() - check if sta+sap scc + * allowed on dfs chan + * @psoc: pointer to soc + * This function is used to check if sta+sap scc allowed on dfs channel + * + * Return: true if sta+sap scc is allowed on dfs channel, otherwise false + */ +bool policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan( + struct wlan_objmgr_psoc *psoc); +/** + * policy_mgr_is_sta_connected_2g() - check if sta connected in 2g + * @psoc: pointer to soc + * + * Return: true if sta is connected in 2g else false + */ +bool policy_mgr_is_sta_connected_2g(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_trim_acs_channel_list() - Trim the ACS channel list based + * on the number of active station connections + * @org_ch_list: ACS channel list from user space + * @org_ch_list_count: ACS channel count from user space + * + * Return: None + */ +void policy_mgr_trim_acs_channel_list(struct wlan_objmgr_psoc *psoc, + uint8_t *org_ch_list, uint8_t *org_ch_list_count); + +/** + * policy_mgr_is_hwmode_set_for_given_chnl() - to check for given channel + * if the hw mode is properly set. + * @psoc: pointer to psoc + * @channel: given channel + * + * If HW mode is properly set for given channel then it returns true else + * it returns false. + * For example, when 2x2 DBS is supported and if the first connection is + * coming up on 2G band then driver expects DBS HW mode to be set first + * before the connection can be established. Driver can call this API to + * find-out if HW mode is set properly. + * + * Return: true if HW mode is set properly else false + */ +bool policy_mgr_is_hwmode_set_for_given_chnl(struct wlan_objmgr_psoc *psoc, + uint8_t channel); +/* + * policy_mgr_get_connection_info() - Get info of all active connections + * @info: Pointer to connection info + * + * Return: Connection count + */ +uint32_t policy_mgr_get_connection_info(struct wlan_objmgr_psoc *psoc, + struct connection_info *info); +/** + * policy_mgr_register_mode_change_cb() - Register mode change callback with + * policy manager + * @callback: HDD callback to be registered + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_register_mode_change_cb(struct wlan_objmgr_psoc *psoc, + send_mode_change_event_cb mode_change_cb); +/** + * policy_mgr_deregister_mode_change_cb() - Deregister mode change callback with + * policy manager + * @callback: HDD callback to be registered + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_deregister_mode_change_cb(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_allow_sap_go_concurrency() - check whether SAP/GO concurrency is + * allowed. + * @psoc: pointer to soc + * @policy_mgr_con_mode: operating mode of interface to be checked + * @channel: new operating channel of the interface to be checked + * @vdev_id: vdev id of the connection to be checked, 0xff for new connection + * + * Checks whether new channel SAP/GO can co-exist with the channel of existing + * SAP/GO connection. This API mainly used for two purposes: + * + * 1) When new GO/SAP session is coming up and needs to check if this session's + * channel can co-exist with existing existing GO/SAP sessions. For example, + * when single radio platform comes, MCC for SAP/GO+SAP/GO is not supported, in + * such case this API should prevent bringing the second connection. + * + * 2) There is already existing SAP+GO combination but due to upper layer + * notifying LTE-COEX event or sending command to move one of the connections + * to different channel. In such cases before moving existing connection to new + * channel, check if new channel can co-exist with the other existing + * connection. For example, one SAP1 is on channel-6 and second SAP2 is on + * channel-36 and lets say they are doing DBS, and lets say upper layer sends + * LTE-COEX to move SAP1 from channel-6 to channel-149. In this case, SAP1 and + * SAP2 will end up doing MCC which may not be desirable result. such cases + * will be prevented with this API. + * + * Return: true or false + */ +bool policy_mgr_allow_sap_go_concurrency(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint8_t channel, + uint32_t vdev_id); + +/** + * policy_mgr_dual_beacon_on_single_mac_scc_capable() - get capability that + * whether support dual beacon on same channel on single MAC + * @psoc: pointer to soc + * + * Return: bool: capable + */ +bool policy_mgr_dual_beacon_on_single_mac_scc_capable( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_dual_beacon_on_single_mac_mcc_capable() - get capability that + * whether support dual beacon on different channel on single MAC + * @psoc: pointer to soc + * + * Return: bool: capable + */ +bool policy_mgr_dual_beacon_on_single_mac_mcc_capable( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_sta_sap_scc_on_lte_coex_chan() - get capability that + * whether support sta sap scc on lte coex chan + * @psoc: pointer to soc + * + * Return: bool: capable + */ +bool policy_mgr_sta_sap_scc_on_lte_coex_chan( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_valid_channel_for_channel_switch() - check for valid channel for + * channel switch. + * @psoc: poniter to psoc + * @channel: channel to be validated. + * This function validates whether the given channel is valid for channel + * switch. + * + * Return: true or false + */ +bool policy_mgr_is_valid_for_channel_switch(struct wlan_objmgr_psoc *psoc, + uint8_t channel); + +/** + * policy_mgr_update_user_config_sap_chan() - Update user configured channel + * @psoc: poniter to psoc + * @channel: channel to be upated + * + * Return: void + **/ +void policy_mgr_update_user_config_sap_chan( + struct wlan_objmgr_psoc *psoc, uint32_t channel); + +/** + * policy_mgr_is_sap_restart_required_after_sta_disconnect() - is sap restart + * required + * after sta disconnection + * @psoc: psoc object data + * @intf_ch: sap channel + * + * Check if SAP should be moved to a non dfs channel after STA disconnection. + * This API applicable only for STA+SAP SCC and ini 'sta_sap_scc_on_dfs_chan' + * or 'sta_sap_scc_on_lte_coex_chan' is enabled. + * + * Return: true if sap restart is required, otherwise false + */ +bool policy_mgr_is_sap_restart_required_after_sta_disconnect( + struct wlan_objmgr_psoc *psoc, uint8_t *intf_ch); + +/** + * policy_mgr_is_sta_sap_scc() - check whether SAP is doing SCC with + * STA + * @psoc: poniter to psoc + * @sap_ch: operating channel of SAP interface + * This function checks whether SAP is doing SCC with STA + * + * Return: true or false + */ +bool policy_mgr_is_sta_sap_scc(struct wlan_objmgr_psoc *psoc, uint8_t sap_ch); + +/** + * policy_mgr_get_hw_mode_from_idx() - Get HW mode based on index + * @psoc: psoc object + * @idx: HW mode id + * @hw_mode: HW mode params + * + * Fetches the HW mode parameters + * + * Return: Success if hw mode is obtained and the hw mode params + */ +QDF_STATUS policy_mgr_get_hw_mode_from_idx( + struct wlan_objmgr_psoc *psoc, + uint32_t idx, + struct policy_mgr_hw_mode_params *hw_mode); +#endif /* __WLAN_POLICY_MGR_API_H */ diff --git a/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_cfg.h b/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_cfg.h new file mode 100644 index 000000000000..22900022d06e --- /dev/null +++ b/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_cfg.h @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __CFG_POLICY_MGR +#define __CFG_POLICY_MGR +#include "qdf_types.h" + +/* + * + * gWlanMccToSccSwitchMode - Control SAP channel. + * @Min: 0 + * @Max: 5 + * @Default: 0 + * + * This ini is used to override SAP channel. + * If gWlanMccToSccSwitchMode = 0: disabled. + * If gWlanMccToSccSwitchMode = 1: Enable switch. + * If gWlainMccToSccSwitchMode = 2: Force switch with SAP restart. + * If gWlainMccToSccSwitchMode = 3: Force switch without SAP restart. + * If gWlainMccToSccSwitchMode = 4: Switch using + * fav channel(s)without SAP restart. + * If gWlainMccToSccSwitchMode = 5: Force switch without SAP restart.MCC allowed + * in exceptional cases. + * If gWlainMccToSccSwitchMode = 6: Force Switch without SAP restart only in + user preffered band. + * Related: None. + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define CFG_MCC_TO_SCC_SWITCH CFG_INI_UINT(\ + "gWlanMccToSccSwitchMode", \ + QDF_MCC_TO_SCC_SWITCH_DISABLE, \ + QDF_MCC_TO_SCC_SWITCH_MAX - 1, \ + QDF_MCC_TO_SCC_SWITCH_DISABLE, \ + CFG_VALUE_OR_DEFAULT, \ + "Provides MCC to SCC switch mode") +/* + * + * gSystemPref - Configure wlan system preference for PCL. + * @Min: 0 + * @Max: 2 + * @Default: 0 + * + * This ini is used to configure wlan system preference option to help + * policy manager decide on Preferred Channel List for a new connection. + * For possible values refer to enum hdd_conc_priority_mode + * + * Related: None. + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define CFG_CONC_SYS_PREF CFG_INI_UINT(\ + "gSystemPref", 0, 2, 0, \ + CFG_VALUE_OR_DEFAULT, \ + "System preference to predict PCL") +/* + * + * gMaxConcurrentActiveSessions - Maximum number of concurrent connections. + * @Min: 1 + * @Max: 4 + * @Default: 3 + * + * This ini is used to configure the maximum number of concurrent connections. + * + * Related: None. + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define CFG_MAX_CONC_CXNS CFG_INI_UINT(\ + "gMaxConcurrentActiveSessions", \ + 1, 4, 3, \ + CFG_VALUE_OR_DEFAULT, \ + "Config max num allowed connections") +/* + * + * channel_select_logic_conc - Set channel selection logic + * for different concurrency combinations to DBS or inter band + * MCC. Default is DBS for STA+STA and STA+P2P. + * @Min: 0x00000000 + * @Max: 0xFFFFFFFF + * @Default: 0x00000000 + * + * 0 - inter-band MCC + * 1 - DBS + * + * BIT 0: STA+STA + * BIT 1: STA+P2P + * BIT 2-31: Reserved + * + * Supported Feature: STA+STA, STA+P2P + * + * Usage: External + * + * + */ +#define CFG_CHNL_SELECT_LOGIC_CONC CFG_INI_UINT(\ + "channel_select_logic_conc",\ + 0x00000000, \ + 0xFFFFFFFF, \ + 0x00000003, \ + CFG_VALUE_OR_DEFAULT, \ + "Set channel selection policy for various concurrency") +/* + * + * dbs_selection_policy - Configure dbs selection policy. + * @Min: 0 + * @Max: 3 + * @Default: 0 + * + * set band preference or Vdev preference. + * bit[0] = 0: 5G 2x2 preferred to select 2x2 5G + 1x1 2G DBS mode. + * bit[0] = 1: 2G 2x2 preferred to select 2x2 2G + 1x1 5G DBS mode. + * bit[1] = 1: vdev priority enabled. The INI "vdev_priority_list" will + * specify the vdev priority. + * bit[1] = 0: vdev priority disabled. + * This INI only take effect for Genoa dual DBS hw. + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define CFG_DBS_SELECTION_PLCY CFG_INI_UINT(\ + "dbs_selection_policy", \ + 0, 3, 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Configure dbs selection policy") +/* + * + * vdev_priority_list - Configure vdev priority list. + * @Min: 0 + * @Max: 0x4444 + * @Default: 0x4321 + * + * @vdev_priority_list: vdev priority list + * bit[0-3]: pri_id (policy_mgr_pri_id) of highest priority + * bit[4-7]: pri_id (policy_mgr_pri_id) of second priority + * bit[8-11]: pri_id (policy_mgr_pri_id) of third priority + * bit[12-15]: pri_id (policy_mgr_pri_id) of fourth priority + * example: 0x4321 - CLI < GO < SAP < STA + * vdev priority id mapping: + * PM_STA_PRI_ID = 1, + * PM_SAP_PRI_ID = 2, + * PM_P2P_GO_PRI_ID = 3, + * PM_P2P_CLI_PRI_ID = 4, + * When the previous INI "dbs_selection_policy" bit[1]=1, which means + * the vdev 2x2 prioritization enabled. Then this INI will be used to + * specify the vdev type priority list. For example : + * dbs_selection_policy=0x2 + * vdev_priority_list=0x4312 + * means: default preference 2x2 band is 5G, vdev 2x2 prioritization enabled. + * And the priority list is CLI < GO < STA < SAP + * + * This INI only take effect for Genoa dual DBS hw. + * + * Supported Feature: DBS + * + * Usage: Internal/External + * + * + */ +#define CFG_VDEV_CUSTOM_PRIORITY_LIST CFG_INI_UINT(\ + "vdev_priority_list", \ + 0, 0x4444, 0x4321, \ + CFG_VALUE_OR_DEFAULT, \ + "Configure vdev priority list") +/* + * + * gEnableCustomConcRule1 - Enable custom concurrency rule1. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable custom concurrency rule1. + * If SAP comes up first and STA comes up later then SAP needs to follow STA's + * channel. + * + * Related: None. + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_CONC_RULE1 CFG_INI_UINT(\ + "gEnableCustomConcRule1", \ + 0, 1, 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable custom concurrency rule 1") +/* + * + * gEnableCustomConcRule2 - Enable custom concurrency rule2. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable custom concurrency rule2. + * If P2PGO comes up first and STA comes up later then P2PGO need to follow + * STA's channel in 5Ghz. In following if condition we are just adding sanity + * check to make sure that by this time P2PGO's channel is same as STA's + * channel. + * + * Related: None. + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_CONC_RULE2 CFG_INI_UINT(\ + "gEnableCustomConcRule2", \ + 0, 1, 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable custom concurrency rule 2") +/* + * + * gEnableMCCAdaptiveScheduler - MCC Adaptive Scheduler feature. + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable MCC Adaptive Scheduler feature. + * + * Related: None. + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_MCC_ADATIVE_SCH_ENABLED_NAME CFG_INI_UINT(\ + "gEnableMCCAdaptiveScheduler", \ + 0, 1, 1, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable/Disable MCC Adaptive Scheduler") + +/* + * + * gEnableStaConnectionIn5Ghz - To enable/disable STA connection in 5G + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable STA connection in 5G band + * + * Related: STA + * + * Supported Feature: Concurrency + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_STA_CONNECTION_IN_5GHZ CFG_INI_UINT(\ + "gEnableStaConnectionIn5Ghz", \ + 0, 1, 1, \ + CFG_VALUE_OR_DEFAULT, \ + "Enable/Disable STA connection in 5G") + +#define CFG_POLICY_MGR_ALL \ + CFG(CFG_MCC_TO_SCC_SWITCH) \ + CFG(CFG_CONC_SYS_PREF) \ + CFG(CFG_MAX_CONC_CXNS) \ + CFG(CFG_DBS_SELECTION_PLCY) \ + CFG(CFG_VDEV_CUSTOM_PRIORITY_LIST) \ + CFG(CFG_CHNL_SELECT_LOGIC_CONC) \ + CFG(CFG_ENABLE_CONC_RULE1) \ + CFG(CFG_ENABLE_CONC_RULE2) \ + CFG(CFG_ENABLE_MCC_ADATIVE_SCH_ENABLED_NAME)\ + CFG(CFG_ENABLE_STA_CONNECTION_IN_5GHZ) +#endif diff --git a/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_public_struct.h b/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_public_struct.h new file mode 100644 index 000000000000..182afded647a --- /dev/null +++ b/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_public_struct.h @@ -0,0 +1,1091 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_POLICY_MGR_PUBLIC_STRUCT_H +#define __WLAN_POLICY_MGR_PUBLIC_STRUCT_H + +/** + * DOC: wlan_policy_mgr_public_struct.h + * + * Concurrenct Connection Management entity + */ + +/* Include files */ +#include + +#define POLICY_MGR_MAX_CHANNEL_LIST 128 + +/** + * Some max value greater than the max length of the channel list + */ +#define MAX_WEIGHT_OF_PCL_CHANNELS 255 +/** + * Some fixed weight difference between the groups + */ +#define PCL_GROUPS_WEIGHT_DIFFERENCE 20 + +/** + * Currently max, only 3 groups are possible as per 'enum policy_mgr_pcl_type'. + * i.e., in a PCL only 3 groups of channels can be present + * e.g., SCC channel on 2.4 Ghz, SCC channel on 5 Ghz & 5 Ghz channels. + * Group 1 has highest priority, group 2 has the next higher priority + * and so on. + */ +#define WEIGHT_OF_GROUP1_PCL_CHANNELS MAX_WEIGHT_OF_PCL_CHANNELS +#define WEIGHT_OF_GROUP2_PCL_CHANNELS \ + (WEIGHT_OF_GROUP1_PCL_CHANNELS - PCL_GROUPS_WEIGHT_DIFFERENCE) +#define WEIGHT_OF_GROUP3_PCL_CHANNELS \ + (WEIGHT_OF_GROUP2_PCL_CHANNELS - PCL_GROUPS_WEIGHT_DIFFERENCE) +#define WEIGHT_OF_GROUP4_PCL_CHANNELS \ + (WEIGHT_OF_GROUP3_PCL_CHANNELS - PCL_GROUPS_WEIGHT_DIFFERENCE) + +#define WEIGHT_OF_NON_PCL_CHANNELS 1 +#define WEIGHT_OF_DISALLOWED_CHANNELS 0 + +#define MAX_MAC 2 + +#define MAX_NUMBER_OF_CONC_CONNECTIONS 3 + +typedef int (*send_mode_change_event_cb)(void); + +/** + * enum hw_mode_ss_config - Possible spatial stream configuration + * @HW_MODE_SS_0x0: Unused Tx and Rx of MAC + * @HW_MODE_SS_1x1: 1 Tx SS and 1 Rx SS + * @HW_MODE_SS_2x2: 2 Tx SS and 2 Rx SS + * @HW_MODE_SS_3x3: 3 Tx SS and 3 Rx SS + * @HW_MODE_SS_4x4: 4 Tx SS and 4 Rx SS + * + * Note: Right now only 1x1 and 2x2 are being supported. Other modes should + * be added when supported. Asymmetric configuration like 1x2, 2x1 are also + * not supported now. But, they are still valid. Right now, Tx/Rx SS support is + * 4 bits long. So, we can go upto 15x15 + */ +enum hw_mode_ss_config { + HW_MODE_SS_0x0, + HW_MODE_SS_1x1, + HW_MODE_SS_2x2, + HW_MODE_SS_3x3, + HW_MODE_SS_4x4, +}; + +/** + * enum hw_mode_dbs_capab - DBS HW mode capability + * @HW_MODE_DBS_NONE: Non DBS capable + * @HW_MODE_DBS: DBS capable + */ +enum hw_mode_dbs_capab { + HW_MODE_DBS_NONE, + HW_MODE_DBS, +}; + +/** + * enum hw_mode_agile_dfs_capab - Agile DFS HW mode capability + * @HW_MODE_AGILE_DFS_NONE: Non Agile DFS capable + * @HW_MODE_AGILE_DFS: Agile DFS capable + */ +enum hw_mode_agile_dfs_capab { + HW_MODE_AGILE_DFS_NONE, + HW_MODE_AGILE_DFS, +}; + +/** + * enum hw_mode_sbs_capab - SBS HW mode capability + * @HW_MODE_SBS_NONE: Non SBS capable + * @HW_MODE_SBS: SBS capable + */ +enum hw_mode_sbs_capab { + HW_MODE_SBS_NONE, + HW_MODE_SBS, +}; + +/** + * enum hw_mode_mac_band_cap - mac band capability + * @HW_MODE_MAC_BAND_NONE: No band requirement. + * @HW_MODE_MAC_BAND_2G: 2G band supported. + * @HW_MODE_MAC_BAND_5G: 5G band supported. + * + * To add HW_MODE_MAC_BAND_NONE value to help to + * match the HW DBS mode in hw mode list. + * Other enum values should match with WMI header: + * typedef enum { + * WLAN_2G_CAPABILITY = 0x1, + * WLAN_5G_CAPABILITY = 0x2, + * } WLAN_BAND_CAPABILITY; + */ +enum hw_mode_mac_band_cap { + HW_MODE_MAC_BAND_NONE = 0, + HW_MODE_MAC_BAND_2G = WLAN_2G_CAPABILITY, + HW_MODE_MAC_BAND_5G = WLAN_5G_CAPABILITY, +}; + +/** + * enum policy_mgr_pcl_group_id - Identifies the pcl groups to be used + * @POLICY_MGR_PCL_GROUP_ID1_ID2: Use weights of group1 and group2 + * @POLICY_MGR_PCL_GROUP_ID2_ID3: Use weights of group2 and group3 + * @POLICY_MGR_PCL_GROUP_ID3_ID4: Use weights of group3 and group4 + * + * Since maximum of three groups are possible, this will indicate which + * PCL group needs to be used. + */ +enum policy_mgr_pcl_group_id { + POLICY_MGR_PCL_GROUP_ID1_ID2, + POLICY_MGR_PCL_GROUP_ID2_ID3, + POLICY_MGR_PCL_GROUP_ID3_ID4, +}; + +/** + * policy_mgr_pcl_channel_order - Order in which the PCL is requested + * @POLICY_MGR_PCL_ORDER_NONE: no order + * @POLICY_MGR_PCL_ORDER_24G_THEN_5G: 2.4 Ghz channel followed by 5 Ghz channel + * @POLICY_MGR_PCL_ORDER_5G_THEN_2G: 5 Ghz channel followed by 2.4 Ghz channel + * + * Order in which the PCL is requested + */ +enum policy_mgr_pcl_channel_order { + POLICY_MGR_PCL_ORDER_NONE, + POLICY_MGR_PCL_ORDER_24G_THEN_5G, + POLICY_MGR_PCL_ORDER_5G_THEN_2G, +}; + +/** + * enum policy_mgr_max_rx_ss - Maximum number of receive spatial streams + * @POLICY_MGR_RX_NSS_1: Receive Nss = 1 + * @POLICY_MGR_RX_NSS_2: Receive Nss = 2 + * @POLICY_MGR_RX_NSS_3: Receive Nss = 3 + * @POLICY_MGR_RX_NSS_4: Receive Nss = 4 + * @POLICY_MGR_RX_NSS_5: Receive Nss = 5 + * @POLICY_MGR_RX_NSS_6: Receive Nss = 6 + * @POLICY_MGR_RX_NSS_7: Receive Nss = 7 + * @POLICY_MGR_RX_NSS_8: Receive Nss = 8 + * + * Indicates the maximum number of spatial streams that the STA can receive + */ +enum policy_mgr_max_rx_ss { + POLICY_MGR_RX_NSS_1 = 0, + POLICY_MGR_RX_NSS_2 = 1, + POLICY_MGR_RX_NSS_3 = 2, + POLICY_MGR_RX_NSS_4 = 3, + POLICY_MGR_RX_NSS_5 = 4, + POLICY_MGR_RX_NSS_6 = 5, + POLICY_MGR_RX_NSS_7 = 6, + POLICY_MGR_RX_NSS_8 = 7, + POLICY_MGR_RX_NSS_MAX, +}; + +/** + * enum policy_mgr_chain_mode - Chain Mask tx & rx combination. + * + * @POLICY_MGR_ONE_ONE: One for Tx, One for Rx + * @POLICY_MGR_TWO_TWO: Two for Tx, Two for Rx + * @POLICY_MGR_MAX_NO_OF_CHAIN_MODE: Max place holder + * + * These are generic IDs that identify the various roles + * in the software system + */ +enum policy_mgr_chain_mode { + POLICY_MGR_ONE_ONE = 0, + POLICY_MGR_TWO_TWO, + POLICY_MGR_MAX_NO_OF_CHAIN_MODE +}; + +/** + * enum policy_mgr_conc_priority_mode - t/p, powersave, latency. + * + * @PM_THROUGHPUT: t/p is the priority + * @PM_POWERSAVE: powersave is the priority + * @PM_LATENCY: latency is the priority + * @PM_MAX_CONC_PRIORITY_MODE: Max place holder + * + * These are generic IDs that identify the various roles + * in the software system + */ +enum policy_mgr_conc_priority_mode { + PM_THROUGHPUT = 0, + PM_POWERSAVE, + PM_LATENCY, + PM_MAX_CONC_PRIORITY_MODE +}; + +/** + * enum policy_mgr_con_mode - concurrency mode for PCL table + * + * @PM_STA_MODE: station mode + * @PM_SAP_MODE: SAP mode + * @PM_P2P_CLIENT_MODE: P2P client mode + * @PM_P2P_GO_MODE: P2P Go mode + * @PM_IBSS_MODE: IBSS mode + * @PM_NDI_MODE: NDI mode + * @PM_MAX_NUM_OF_MODE: max value place holder + */ +enum policy_mgr_con_mode { + PM_STA_MODE = 0, + PM_SAP_MODE, + PM_P2P_CLIENT_MODE, + PM_P2P_GO_MODE, + PM_IBSS_MODE, + PM_NDI_MODE, + PM_MAX_NUM_OF_MODE +}; + +/** + * enum policy_mgr_mac_use - MACs that are used + * @POLICY_MGR_MAC0: Only MAC0 is used + * @POLICY_MGR_MAC1: Only MAC1 is used + * @POLICY_MGR_MAC0_AND_MAC1: Both MAC0 and MAC1 are used + */ +enum policy_mgr_mac_use { + POLICY_MGR_MAC0 = 1, + POLICY_MGR_MAC1 = 2, + POLICY_MGR_MAC0_AND_MAC1 = 3 +}; + +/** + * enum policy_mgr_pcl_type - Various types of Preferred channel list (PCL). + * + * @PM_NONE: No channel preference + * @PM_24G: 2.4 Ghz channels only + * @PM_5G: 5 Ghz channels only + * @PM_SCC_CH: SCC channel only + * @PM_MCC_CH: MCC channels only + * @PM_SBS_CH: SBS channels only + * @PM_SCC_CH_24G: SCC channel & 2.4 Ghz channels + * @PM_SCC_CH_5G: SCC channel & 5 Ghz channels + * @PM_24G_SCC_CH: 2.4 Ghz channels & SCC channel + * @PM_5G_SCC_CH: 5 Ghz channels & SCC channel + * @PM_SCC_ON_5_SCC_ON_24_24G: SCC channel on 5 Ghz, SCC + * channel on 2.4 Ghz & 2.4 Ghz channels + * @PM_SCC_ON_5_SCC_ON_24_5G: SCC channel on 5 Ghz, SCC channel + * on 2.4 Ghz & 5 Ghz channels + * @PM_SCC_ON_24_SCC_ON_5_24G: SCC channel on 2.4 Ghz, SCC + * channel on 5 Ghz & 2.4 Ghz channels + * @PM_SCC_ON_24_SCC_ON_5_5G: SCC channel on 2.4 Ghz, SCC + * channel on 5 Ghz & 5 Ghz channels + * @PM_SCC_ON_5_SCC_ON_24: SCC channel on 5 Ghz, SCC channel on + * 2.4 Ghz + * @PM_SCC_ON_24_SCC_ON_5: SCC channel on 2.4 Ghz, SCC channel + * on 5 Ghz + * @PM_MCC_CH_24G: MCC channels & 2.4 Ghz channels + * @PM_MCC_CH_5G: MCC channels & 5 Ghz channels + * @PM_24G_MCC_CH: 2.4 Ghz channels & MCC channels + * @PM_5G_MCC_CH: 5 Ghz channels & MCC channels + * @PM_SBS_CH_5G: SBS channels & rest of 5 Ghz channels + * @PM_24G_SCC_CH_SBS_CH: 2.4 Ghz channels, SCC channel & SBS channels + * @PM_24G_SCC_CH_SBS_CH_5G: 2.4 Ghz channels, SCC channel, + * SBS channels & rest of the 5G channels + * @PM_24G_SBS_CH_MCC_CH: 2.4 Ghz channels, SBS channels & MCC channels + * @PM_MAX_PCL_TYPE: Max place holder + * + * These are generic IDs that identify the various roles + * in the software system + */ +enum policy_mgr_pcl_type { + PM_NONE = 0, + PM_24G, + PM_5G, + PM_SCC_CH, + PM_MCC_CH, + PM_SBS_CH, + PM_SCC_CH_24G, + PM_SCC_CH_5G, + PM_24G_SCC_CH, + PM_5G_SCC_CH, + PM_SCC_ON_5_SCC_ON_24_24G, + PM_SCC_ON_5_SCC_ON_24_5G, + PM_SCC_ON_24_SCC_ON_5_24G, + PM_SCC_ON_24_SCC_ON_5_5G, + PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_24_SCC_ON_5, + PM_MCC_CH_24G, + PM_MCC_CH_5G, + PM_24G_MCC_CH, + PM_5G_MCC_CH, + PM_SBS_CH_5G, + PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH_5G, + PM_24G_SBS_CH_MCC_CH, + + PM_MAX_PCL_TYPE +}; + +/** + * enum policy_mgr_one_connection_mode - Combination of first connection + * type, band & spatial stream used. + * + * @PM_STA_24_1x1: STA connection using 1x1@2.4 Ghz + * @PM_STA_24_2x2: STA connection using 2x2@2.4 Ghz + * @PM_STA_5_1x1: STA connection using 1x1@5 Ghz + * @PM_STA_5_2x2: STA connection using 2x2@5 Ghz + * @PM_P2P_CLI_24_1x1: P2P Client connection using 1x1@2.4 Ghz + * @PM_P2P_CLI_24_2x2: P2P Client connection using 2x2@2.4 Ghz + * @PM_P2P_CLI_5_1x1: P2P Client connection using 1x1@5 Ghz + * @PM_P2P_CLI_5_2x2: P2P Client connection using 2x2@5 Ghz + * @PM_P2P_GO_24_1x1: P2P GO connection using 1x1@2.4 Ghz + * @PM_P2P_GO_24_2x2: P2P GO connection using 2x2@2.4 Ghz + * @PM_P2P_GO_5_1x1: P2P GO connection using 1x1@5 Ghz + * @PM_P2P_GO_5_2x2: P2P GO connection using 2x2@5 Ghz + * @PM_SAP_24_1x1: SAP connection using 1x1@2.4 Ghz + * @PM_SAP_24_2x2: SAP connection using 2x2@2.4 Ghz + * @PM_SAP_5_1x1: SAP connection using 1x1@5 Ghz + * @PM_SAP_5_1x1: SAP connection using 2x2@5 Ghz + * @PM_IBSS_24_1x1: IBSS connection using 1x1@2.4 Ghz + * @PM_IBSS_24_2x2: IBSS connection using 2x2@2.4 Ghz + * @PM_IBSS_5_1x1: IBSS connection using 1x1@5 Ghz + * @PM_IBSS_5_2x2: IBSS connection using 2x2@5 Ghz + * @PM_MAX_ONE_CONNECTION_MODE: Max place holder + * + * These are generic IDs that identify the various roles + * in the software system + */ +enum policy_mgr_one_connection_mode { + PM_STA_24_1x1 = 0, + PM_STA_24_2x2, + PM_STA_5_1x1, + PM_STA_5_2x2, + PM_P2P_CLI_24_1x1, + PM_P2P_CLI_24_2x2, + PM_P2P_CLI_5_1x1, + PM_P2P_CLI_5_2x2, + PM_P2P_GO_24_1x1, + PM_P2P_GO_24_2x2, + PM_P2P_GO_5_1x1, + PM_P2P_GO_5_2x2, + PM_SAP_24_1x1, + PM_SAP_24_2x2, + PM_SAP_5_1x1, + PM_SAP_5_2x2, + PM_IBSS_24_1x1, + PM_IBSS_24_2x2, + PM_IBSS_5_1x1, + PM_IBSS_5_2x2, + + PM_MAX_ONE_CONNECTION_MODE +}; + +/** + * enum policy_mgr_two_connection_mode - Combination of first two + * connections type, concurrency state, band & spatial stream + * used. + * + * @PM_STA_SAP_SCC_24_1x1: STA & SAP connection on SCC using + * 1x1@2.4 Ghz + * @PM_STA_SAP_SCC_24_2x2: STA & SAP connection on SCC using + * 2x2@2.4 Ghz + * @PM_STA_SAP_MCC_24_1x1: STA & SAP connection on MCC using + * 1x1@2.4 Ghz + * @PM_STA_SAP_MCC_24_2x2: STA & SAP connection on MCC using + * 2x2@2.4 Ghz + * @PM_STA_SAP_SCC_5_1x1: STA & SAP connection on SCC using + * 1x1@5 Ghz + * @PM_STA_SAP_SCC_5_2x2: STA & SAP connection on SCC using + * 2x2@5 Ghz + * @PM_STA_SAP_MCC_5_1x1: STA & SAP connection on MCC using + * 1x1@5 Ghz + * @PM_STA_SAP_MCC_5_2x2: STA & SAP connection on MCC using + * 2x2@5 Ghz + * @PM_STA_SAP_DBS_1x1: STA & SAP connection on DBS using 1x1 + * @PM_STA_SAP_DBS_2x2: STA & SAP connection on DBS using 2x2 + * @PM_STA_SAP_SBS_5_1x1: STA & SAP connection on 5G SBS using 1x1 + * @PM_STA_P2P_GO_SCC_24_1x1: STA & P2P GO connection on SCC + * using 1x1@2.4 Ghz + * @PM_STA_P2P_GO_SCC_24_2x2: STA & P2P GO connection on SCC + * using 2x2@2.4 Ghz + * @PM_STA_P2P_GO_MCC_24_1x1: STA & P2P GO connection on MCC + * using 1x1@2.4 Ghz + * @PM_STA_P2P_GO_MCC_24_2x2: STA & P2P GO connection on MCC + * using 2x2@2.4 Ghz + * @PM_STA_P2P_GO_SCC_5_1x1: STA & P2P GO connection on SCC + * using 1x1@5 Ghz + * @PM_STA_P2P_GO_SCC_5_2x2: STA & P2P GO connection on SCC + * using 2x2@5 Ghz + * @PM_STA_P2P_GO_MCC_5_1x1: STA & P2P GO connection on MCC + * using 1x1@5 Ghz + * @PM_STA_P2P_GO_MCC_5_2x2: STA & P2P GO connection on MCC + * using 2x2@5 Ghz + * @PM_STA_P2P_GO_DBS_1x1: STA & P2P GO connection on DBS using + * 1x1 + * @PM_STA_P2P_GO_DBS_2x2: STA & P2P GO connection on DBS using + * 2x2 + * @PM_STA_P2P_GO_SBS_5_1x1: STA & P2P GO connection on 5G SBS + * using 1x1 + * @PM_STA_P2P_CLI_SCC_24_1x1: STA & P2P CLI connection on SCC + * using 1x1@2.4 Ghz + * @PM_STA_P2P_CLI_SCC_24_2x2: STA & P2P CLI connection on SCC + * using 2x2@2.4 Ghz + * @PM_STA_P2P_CLI_MCC_24_1x1: STA & P2P CLI connection on MCC + * using 1x1@2.4 Ghz + * @PM_STA_P2P_CLI_MCC_24_2x2: STA & P2P CLI connection on MCC + * using 2x2@2.4 Ghz + * @PM_STA_P2P_CLI_SCC_5_1x1: STA & P2P CLI connection on SCC + * using 1x1@5 Ghz + * @PM_STA_P2P_CLI_SCC_5_2x2: STA & P2P CLI connection on SCC + * using 2x2@5 Ghz + * @PM_STA_P2P_CLI_MCC_5_1x1: STA & P2P CLI connection on MCC + * using 1x1@5 Ghz + * @PM_STA_P2P_CLI_MCC_5_2x2: STA & P2P CLI connection on MCC + * using 2x2@5 Ghz + * @PM_STA_P2P_CLI_DBS_1x1: STA & P2P CLI connection on DBS + * using 1x1 + * @PM_STA_P2P_CLI_DBS_2x2: STA & P2P CLI connection on DBS + * using 2x2 + * @PM_STA_P2P_CLI_SBS_5_1x1: STA & P2P CLI connection on 5G + * SBS using 1x1 + * @PM_P2P_GO_P2P_CLI_SCC_24_1x1: P2P GO & CLI connection on + * SCC using 1x1@2.4 Ghz + * @PM_P2P_GO_P2P_CLI_SCC_24_2x2: P2P GO & CLI connection on + * SCC using 2x2@2.4 Ghz + * @PM_P2P_GO_P2P_CLI_MCC_24_1x1: P2P GO & CLI connection on + * MCC using 1x1@2.4 Ghz + * @PM_P2P_GO_P2P_CLI_MCC_24_2x2: P2P GO & CLI connection on + * MCC using 2x2@2.4 Ghz + * @PM_P2P_GO_P2P_CLI_SCC_5_1x1: P2P GO & CLI connection on + * SCC using 1x1@5 Ghz + * @PM_P2P_GO_P2P_CLI_SCC_5_2x2: P2P GO & CLI connection on + * SCC using 2x2@5 Ghz + * @PM_P2P_GO_P2P_CLI_MCC_5_1x1: P2P GO & CLI connection on + * MCC using 1x1@5 Ghz + * @PM_P2P_GO_P2P_CLI_MCC_5_2x2: P2P GO & CLI connection on + * MCC using 2x2@5 Ghz + * @PM_P2P_GO_P2P_CLI_DBS_1x1: P2P GO & CLI connection on DBS + * using 1x1 + * @PM_P2P_GO_P2P_CLI_DBS_2x2: P2P GO & P2P CLI connection + * on DBS using 2x2 + * @PM_P2P_GO_P2P_CLI_SBS_5_1x1: P2P GO & P2P CLI connection + * on 5G SBS using 1x1 + * @PM_P2P_GO_SAP_SCC_24_1x1: P2P GO & SAP connection on + * SCC using 1x1@2.4 Ghz + * @PM_P2P_GO_SAP_SCC_24_2x2: P2P GO & SAP connection on + * SCC using 2x2@2.4 Ghz + * @PM_P2P_GO_SAP_MCC_24_1x1: P2P GO & SAP connection on + * MCC using 1x1@2.4 Ghz + * @PM_P2P_GO_SAP_MCC_24_2x2: P2P GO & SAP connection on + * MCC using 2x2@2.4 Ghz + * @PM_P2P_GO_SAP_SCC_5_1x1: P2P GO & SAP connection on + * SCC using 1x1@5 Ghz + * @PM_P2P_GO_SAP_SCC_5_2x2: P2P GO & SAP connection on + * SCC using 2x2@5 Ghz + * @PM_P2P_GO_SAP_MCC_5_1x1: P2P GO & SAP connection on + * MCC using 1x1@5 Ghz + * @PM_P2P_GO_SAP_MCC_5_2x2: P2P GO & SAP connection on + * MCC using 2x2@5 Ghz + * @PM_P2P_GO_SAP_DBS_1x1: P2P GO & SAP connection on DBS using + * 1x1 + * @PM_P2P_GO_SAP_DBS_2x2: P2P GO & SAP connection on DBS using + * 2x2 + * @PM_P2P_GO_SAP_SBS_5_1x1: P2P GO & SAP connection on 5G SBS + * using 1x1 + * @PM_P2P_CLI_SAP_SCC_24_1x1: CLI & SAP connection on SCC using + * 1x1@2.4 Ghz + * @PM_P2P_CLI_SAP_SCC_24_2x2: CLI & SAP connection on SCC using + * 2x2@2.4 Ghz + * @PM_P2P_CLI_SAP_MCC_24_1x1: CLI & SAP connection on MCC using + * 1x1@2.4 Ghz + * @PM_P2P_CLI_SAP_MCC_24_2x2: CLI & SAP connection on MCC using + * 2x2@2.4 Ghz + * @PM_P2P_CLI_SAP_SCC_5_1x1: CLI & SAP connection on SCC using + * 1x1@5 Ghz + * @PM_P2P_CLI_SAP_SCC_5_2x2: CLI & SAP connection on SCC using + * 2x2@5 Ghz + * @PM_P2P_CLI_SAP_MCC_5_1x1: CLI & SAP connection on MCC using + * 1x1@5 Ghz + * @PM_P2P_CLI_SAP_MCC_5_2x2: CLI & SAP connection on MCC using + * 2x2@5 Ghz + * @POLICY_MGR_P2P_STA_SAP_MCC_24_5_1x1: CLI and SAP connecting on MCC + * in 2.4 and 5GHz 1x1 + * @POLICY_MGR_P2P_STA_SAP_MCC_24_5_2x2: CLI and SAP connecting on MCC + * in 2.4 and 5GHz 2x2 + * @PM_P2P_CLI_SAP_DBS_1x1,: CLI & SAP connection on DBS using 1x1 + * @PM_P2P_CLI_SAP_DBS_2x2: P2P CLI & SAP connection on DBS using + * 2x2 + * @PM_P2P_CLI_SAP_SBS_5_1x1: P2P CLI & SAP connection on 5G SBS + * using 1x1 + * @PM_SAP_SAP_SCC_24_1x1: SAP & SAP connection on + * SCC using 1x1@2.4 Ghz + * @PM_SAP_SAP_SCC_24_2x2: SAP & SAP connection on + * SCC using 2x2@2.4 Ghz + * @PM_SAP_SAP_MCC_24_1x1: SAP & SAP connection on + * MCC using 1x1@2.4 Ghz + * @PM_SAP_SAP_MCC_24_2x2: SAP & SAP connection on + * MCC using 2x2@2.4 Ghz + * @PM_SAP_SAP_SCC_5_1x1: SAP & SAP connection on + * SCC using 1x1@5 Ghz + * @PM_SAP_SAP_SCC_5_2x2: SAP & SAP connection on + * SCC using 2x2@5 Ghz + * @PM_SAP_SAP_MCC_5_1x1: SAP & SAP connection on + * MCC using 1x1@5 Ghz + * @PM_SAP_SAP_MCC_5_2x2: SAP & SAP connection on + * MCC using 2x2@5 Ghz + * @PM_SAP_SAP_MCC_24_5_1x1: SAP & SAP connection on + * MCC in 2.4 and 5GHz 1x1 + * @PM_SAP_SAP_MCC_24_5_2x2: SAP & SAP connection on + * MCC in 2.4 and 5GHz 2x2 + * @PM_SAP_SAP_DBS_1x1: SAP & SAP connection on DBS using + * 1x1 + * @PM_SAP_SAP_DBS_2x2: SAP & SAP connection on DBS using 2x2 + * @PM_SAP_SAP_SBS_5_1x1: SAP & SAP connection on 5G SBS using 1x1 + * @PM_STA_STA_SCC_24_1x1: STA & STA connection on + * SCC using 1x1@2.4 Ghz + * @PM_STA_STA_SCC_24_2x2: STA & STA connection on + * SCC using 2x2@2.4 Ghz + * @PM_STA_STA_MCC_24_1x1: STA & STA connection on + * MCC using 1x1@2.4 Ghz + * @PM_STA_STA_MCC_24_2x2: STA & STA connection on + * MCC using 2x2@2.4 Ghz + * @PM_STA_STA_SCC_5_1x1: STA & STA connection on + * SCC using 1x1@5 Ghz + * @PM_STA_STA_SCC_5_2x2: STA & STA connection on + * SCC using 2x2@5 Ghz + * @PM_STA_STA_MCC_5_1x1: STA & STA connection on + * MCC using 1x1@5 Ghz + * @PM_STA_STA_MCC_5_2x2: STA & STA connection on + * MCC using 2x2@5 Ghz + * @PM_STA_STA_MCC_24_5_1x1: STA & STA connection on + * MCC in 2.4 and 5GHz 1x1 + * @PM_STA_STA_MCC_24_5_2x2: STA & STA connection on + * MCC in 2.4 and 5GHz 2x2 + * @PM_STA_STA_DBS_1x1: STA & STA connection on DBS using + * 1x1 + * @PM_STA_STA_DBS_2x2: STA & STA connection on DBS using 2x2 + * @PM_STA_STA_SBS_5_1x1: STA & STA connection on 5G SBS using 1x1 + * + * These are generic IDs that identify the various roles in the + * software system + */ +enum policy_mgr_two_connection_mode { + PM_STA_SAP_SCC_24_1x1 = 0, + PM_STA_SAP_SCC_24_2x2, + PM_STA_SAP_MCC_24_1x1, + PM_STA_SAP_MCC_24_2x2, + PM_STA_SAP_SCC_5_1x1, + PM_STA_SAP_SCC_5_2x2, + PM_STA_SAP_MCC_5_1x1, + PM_STA_SAP_MCC_5_2x2, + PM_STA_SAP_MCC_24_5_1x1, + PM_STA_SAP_MCC_24_5_2x2, + PM_STA_SAP_DBS_1x1, + PM_STA_SAP_DBS_2x2, + PM_STA_SAP_SBS_5_1x1, + PM_STA_P2P_GO_SCC_24_1x1, + PM_STA_P2P_GO_SCC_24_2x2, + PM_STA_P2P_GO_MCC_24_1x1, + PM_STA_P2P_GO_MCC_24_2x2, + PM_STA_P2P_GO_SCC_5_1x1, + PM_STA_P2P_GO_SCC_5_2x2, + PM_STA_P2P_GO_MCC_5_1x1, + PM_STA_P2P_GO_MCC_5_2x2, + PM_STA_P2P_GO_MCC_24_5_1x1, + PM_STA_P2P_GO_MCC_24_5_2x2, + PM_STA_P2P_GO_DBS_1x1, + PM_STA_P2P_GO_DBS_2x2, + PM_STA_P2P_GO_SBS_5_1x1, + PM_STA_P2P_CLI_SCC_24_1x1, + PM_STA_P2P_CLI_SCC_24_2x2, + PM_STA_P2P_CLI_MCC_24_1x1, + PM_STA_P2P_CLI_MCC_24_2x2, + PM_STA_P2P_CLI_SCC_5_1x1, + PM_STA_P2P_CLI_SCC_5_2x2, + PM_STA_P2P_CLI_MCC_5_1x1, + PM_STA_P2P_CLI_MCC_5_2x2, + PM_STA_P2P_CLI_MCC_24_5_1x1, + PM_STA_P2P_CLI_MCC_24_5_2x2, + PM_STA_P2P_CLI_DBS_1x1, + PM_STA_P2P_CLI_DBS_2x2, + PM_STA_P2P_CLI_SBS_5_1x1, + PM_P2P_GO_P2P_CLI_SCC_24_1x1, + PM_P2P_GO_P2P_CLI_SCC_24_2x2, + PM_P2P_GO_P2P_CLI_MCC_24_1x1, + PM_P2P_GO_P2P_CLI_MCC_24_2x2, + PM_P2P_GO_P2P_CLI_SCC_5_1x1, + PM_P2P_GO_P2P_CLI_SCC_5_2x2, + PM_P2P_GO_P2P_CLI_MCC_5_1x1, + PM_P2P_GO_P2P_CLI_MCC_5_2x2, + PM_P2P_GO_P2P_CLI_MCC_24_5_1x1, + PM_P2P_GO_P2P_CLI_MCC_24_5_2x2, + PM_P2P_GO_P2P_CLI_DBS_1x1, + PM_P2P_GO_P2P_CLI_DBS_2x2, + PM_P2P_GO_P2P_CLI_SBS_5_1x1, + PM_P2P_GO_SAP_SCC_24_1x1, + PM_P2P_GO_SAP_SCC_24_2x2, + PM_P2P_GO_SAP_MCC_24_1x1, + PM_P2P_GO_SAP_MCC_24_2x2, + PM_P2P_GO_SAP_SCC_5_1x1, + PM_P2P_GO_SAP_SCC_5_2x2, + PM_P2P_GO_SAP_MCC_5_1x1, + PM_P2P_GO_SAP_MCC_5_2x2, + PM_P2P_GO_SAP_MCC_24_5_1x1, + PM_P2P_GO_SAP_MCC_24_5_2x2, + PM_P2P_GO_SAP_DBS_1x1, + PM_P2P_GO_SAP_DBS_2x2, + PM_P2P_GO_SAP_SBS_5_1x1, + PM_P2P_CLI_SAP_SCC_24_1x1, + PM_P2P_CLI_SAP_SCC_24_2x2, + PM_P2P_CLI_SAP_MCC_24_1x1, + PM_P2P_CLI_SAP_MCC_24_2x2, + PM_P2P_CLI_SAP_SCC_5_1x1, + PM_P2P_CLI_SAP_SCC_5_2x2, + PM_P2P_CLI_SAP_MCC_5_1x1, + PM_P2P_CLI_SAP_MCC_5_2x2, + PM_P2P_CLI_SAP_MCC_24_5_1x1, + PM_P2P_CLI_SAP_MCC_24_5_2x2, + PM_P2P_CLI_SAP_DBS_1x1, + PM_P2P_CLI_SAP_DBS_2x2, + PM_P2P_CLI_SAP_SBS_5_1x1, + PM_SAP_SAP_SCC_24_1x1, + PM_SAP_SAP_SCC_24_2x2, + PM_SAP_SAP_MCC_24_1x1, + PM_SAP_SAP_MCC_24_2x2, + PM_SAP_SAP_SCC_5_1x1, + PM_SAP_SAP_SCC_5_2x2, + PM_SAP_SAP_MCC_5_1x1, + PM_SAP_SAP_MCC_5_2x2, + PM_SAP_SAP_MCC_24_5_1x1, + PM_SAP_SAP_MCC_24_5_2x2, + PM_SAP_SAP_DBS_1x1, + PM_SAP_SAP_DBS_2x2, + PM_SAP_SAP_SBS_5_1x1, + PM_STA_STA_SCC_24_1x1, + PM_STA_STA_SCC_24_2x2, + PM_STA_STA_MCC_24_1x1, + PM_STA_STA_MCC_24_2x2, + PM_STA_STA_SCC_5_1x1, + PM_STA_STA_SCC_5_2x2, + PM_STA_STA_MCC_5_1x1, + PM_STA_STA_MCC_5_2x2, + PM_STA_STA_MCC_24_5_1x1, + PM_STA_STA_MCC_24_5_2x2, + PM_STA_STA_DBS_1x1, + PM_STA_STA_DBS_2x2, + PM_STA_STA_SBS_5_1x1, + + PM_MAX_TWO_CONNECTION_MODE +}; + +/** + * enum policy_mgr_conc_next_action - actions to be taken on old + * connections. + * + * @PM_NOP: No action + * @PM_DBS: switch to DBS mode + * @PM_DBS_DOWNGRADE: switch to DBS mode & downgrade to 1x1 + * @PM_DBS_UPGRADE: switch to DBS mode & upgrade to 2x2 + * @PM_SINGLE_MAC: switch to MCC/SCC mode + * @PM_SINGLE_MAC_UPGRADE: switch to MCC/SCC mode & upgrade to 2x2 + * @PM_SBS: switch to SBS mode + * @PM_SBS_DOWNGRADE: switch to SBS mode & downgrade to 1x1 + * @PM_DOWNGRADE: downgrade to 1x1 + * @PM_UPGRADE: upgrade to 2x2 + * @PM_DBS1: switch to DBS 1 + * @PM_DBS1_DOWNGRADE: downgrade 2G beaconing entity to 1x1 and switch to DBS1. + * @PM_DBS2: switch to DBS 2 + * @PM_DBS2_DOWNGRADE: downgrade 5G beaconing entity to 1x1 and switch to DBS2. + * @PM_UPGRADE_5G: upgrade 5g beaconing entity to 2x2. + * @PM_UPGRADE_2G: upgrade 2g beaconing entity to 2x2. + * @PM_MAX_CONC_PRIORITY_MODE: Max place holder + * + * These are generic IDs that identify the various roles + * in the software system + */ +enum policy_mgr_conc_next_action { + PM_NOP = 0, + PM_DBS, + PM_DBS_DOWNGRADE, + PM_DBS_UPGRADE, + PM_SINGLE_MAC, + PM_SINGLE_MAC_UPGRADE, + PM_SBS, + PM_SBS_DOWNGRADE, + PM_DOWNGRADE, + PM_UPGRADE, + PM_DBS1, + PM_DBS1_DOWNGRADE, + PM_DBS2, + PM_DBS2_DOWNGRADE, + PM_UPGRADE_5G, + PM_UPGRADE_2G, + + PM_MAX_CONC_NEXT_ACTION +}; + +/** + * enum policy_mgr_band - wifi band. + * + * @POLICY_MGR_BAND_24: 2.4 Ghz band + * @POLICY_MGR_BAND_5: 5 Ghz band + * @POLICY_MGR_ANY: to specify all band + * @POLICY_MGR_MAX_BAND: Max place holder + * + * These are generic IDs that identify the various roles + * in the software system + */ +enum policy_mgr_band { + POLICY_MGR_BAND_24 = 0, + POLICY_MGR_BAND_5, + POLICY_MGR_ANY, + POLICY_MGR_MAX_BAND = POLICY_MGR_ANY, +}; + +/** + * enum policy_mgr_conn_update_reason: Reason for conc connection update + * @POLICY_MGR_UPDATE_REASON_SET_OPER_CHAN: Set probable operating channel + * @POLICY_MGR_UPDATE_REASON_JOIN_IBSS: Join IBSS + * @POLICY_MGR_UPDATE_REASON_UT: Unit test related + * @POLICY_MGR_UPDATE_REASON_START_AP: Start AP + * @POLICY_MGR_UPDATE_REASON_NORMAL_STA: Connection to Normal STA + * @POLICY_MGR_UPDATE_REASON_HIDDEN_STA: Connection to Hidden STA + * @POLICY_MGR_UPDATE_REASON_OPPORTUNISTIC: Opportunistic HW mode update + * @POLICY_MGR_UPDATE_REASON_NSS_UPDATE: NSS update + * @POLICY_MGR_UPDATE_REASON_CHANNEL_SWITCH: Channel switch + * @POLICY_MGR_UPDATE_REASON_CHANNEL_SWITCH_STA: Channel switch for STA + * @POLICY_MGR_UPDATE_REASON_PRI_VDEV_CHANGE: In Dual DBS HW, if the vdev based + * 2x2 preference enabled, the vdev down may cause prioritized active + * vdev change, then DBS hw mode may needs to change from one DBS mode + * to the other DBS mode. This reason code indicates such condition. + */ +enum policy_mgr_conn_update_reason { + POLICY_MGR_UPDATE_REASON_SET_OPER_CHAN, + POLICY_MGR_UPDATE_REASON_JOIN_IBSS, + POLICY_MGR_UPDATE_REASON_UT, + POLICY_MGR_UPDATE_REASON_START_AP, + POLICY_MGR_UPDATE_REASON_NORMAL_STA, + POLICY_MGR_UPDATE_REASON_HIDDEN_STA, + POLICY_MGR_UPDATE_REASON_OPPORTUNISTIC, + POLICY_MGR_UPDATE_REASON_NSS_UPDATE, + POLICY_MGR_UPDATE_REASON_CHANNEL_SWITCH, + POLICY_MGR_UPDATE_REASON_CHANNEL_SWITCH_STA, + POLICY_MGR_UPDATE_REASON_PRE_CAC, + POLICY_MGR_UPDATE_REASON_PRI_VDEV_CHANGE, +}; + +/** + * enum hw_mode_bandwidth - bandwidth of wifi channel. + * + * @HW_MODE_5_MHZ: 5 Mhz bandwidth + * @HW_MODE_10_MHZ: 10 Mhz bandwidth + * @HW_MODE_20_MHZ: 20 Mhz bandwidth + * @HW_MODE_40_MHZ: 40 Mhz bandwidth + * @HW_MODE_80_MHZ: 80 Mhz bandwidth + * @HW_MODE_80_PLUS_80_MHZ: 80 Mhz plus 80 Mhz bandwidth + * @HW_MODE_160_MHZ: 160 Mhz bandwidth + * @HW_MODE_MAX_BANDWIDTH: Max place holder + * + * These are generic IDs that identify the various roles + * in the software system + */ +enum hw_mode_bandwidth { + HW_MODE_BW_NONE, + HW_MODE_5_MHZ, + HW_MODE_10_MHZ, + HW_MODE_20_MHZ, + HW_MODE_40_MHZ, + HW_MODE_80_MHZ, + HW_MODE_80_PLUS_80_MHZ, + HW_MODE_160_MHZ, + HW_MODE_MAX_BANDWIDTH +}; + +/** + * enum set_hw_mode_status - Status of set HW mode command + * @SET_HW_MODE_STATUS_OK: command successful + * @SET_HW_MODE_STATUS_EINVAL: Requested invalid hw_mode + * @SET_HW_MODE_STATUS_ECANCELED: HW mode change cancelled + * @SET_HW_MODE_STATUS_ENOTSUP: HW mode not supported + * @SET_HW_MODE_STATUS_EHARDWARE: HW mode change prevented by hardware + * @SET_HW_MODE_STATUS_EPENDING: HW mode change is pending + * @SET_HW_MODE_STATUS_ECOEX: HW mode change conflict with Coex + */ +enum set_hw_mode_status { + SET_HW_MODE_STATUS_OK, + SET_HW_MODE_STATUS_EINVAL, + SET_HW_MODE_STATUS_ECANCELED, + SET_HW_MODE_STATUS_ENOTSUP, + SET_HW_MODE_STATUS_EHARDWARE, + SET_HW_MODE_STATUS_EPENDING, + SET_HW_MODE_STATUS_ECOEX, +}; + +typedef void (*dual_mac_cb)(enum set_hw_mode_status status, + uint32_t scan_config, + uint32_t fw_mode_config); +/** + * enum policy_mgr_hw_mode_change - identify the HW mode switching to. + * + * @POLICY_MGR_HW_MODE_NOT_IN_PROGRESS: HW mode change not in progress + * @POLICY_MGR_SMM_IN_PROGRESS: switching to SMM mode + * @POLICY_MGR_DBS_IN_PROGRESS: switching to DBS mode + * @POLICY_MGR_SBS_IN_PROGRESS: switching to SBS mode + * + * These are generic IDs that identify the various roles + * in the software system + */ +enum policy_mgr_hw_mode_change { + POLICY_MGR_HW_MODE_NOT_IN_PROGRESS = 0, + POLICY_MGR_SMM_IN_PROGRESS, + POLICY_MGR_DBS_IN_PROGRESS, + POLICY_MGR_SBS_IN_PROGRESS +}; + +/** + * enum dbs_support - structure to define INI values and their meaning + * + * @ENABLE_DBS_CXN_AND_SCAN: Enable DBS support for connection and scan + * @DISABLE_DBS_CXN_AND_SCAN: Disable DBS support for connection and scan + * @DISABLE_DBS_CXN_AND_ENABLE_DBS_SCAN: disable dbs support for + * connection but keep dbs support for scan + * @DISABLE_DBS_CXN_AND_ENABLE_DBS_SCAN_WITH_ASYNC_SCAN_OFF: disable dbs support + * for connection but keep dbs for scan but switch off the async scan + * @ENABLE_DBS_CXN_AND_ENABLE_SCAN_WITH_ASYNC_SCAN_OFF: enable dbs support for + * connection and scan but switch off the async scan + * @ENABLE_DBS_CXN_AND_DISABLE_DBS_SCAN: Enable DBS support for connection and + * disable DBS support for scan + * @ENABLE_DBS_CXN_AND_DISABLE_SIMULTANEOUS_SCAN: Enable DBS + * support for connection and disable simultaneous scan from + * upper layer (DBS scan remains enabled in FW) + */ +enum dbs_support { + ENABLE_DBS_CXN_AND_SCAN, + DISABLE_DBS_CXN_AND_SCAN, + DISABLE_DBS_CXN_AND_ENABLE_DBS_SCAN, + DISABLE_DBS_CXN_AND_ENABLE_DBS_SCAN_WITH_ASYNC_SCAN_OFF, + ENABLE_DBS_CXN_AND_ENABLE_SCAN_WITH_ASYNC_SCAN_OFF, + ENABLE_DBS_CXN_AND_DISABLE_DBS_SCAN, + ENABLE_DBS_CXN_AND_DISABLE_SIMULTANEOUS_SCAN, +}; + +/** + * struct policy_mgr_conc_connection_info - information of all existing + * connections in the wlan system + * + * @mode: connection type + * @chan: channel of the connection + * @bw: channel bandwidth used for the connection + * @mac: The HW mac it is running + * @chain_mask: The original capability advertised by HW + * @original_nss: nss negotiated at connection time + * @vdev_id: vdev id of the connection + * @in_use: if the table entry is active + */ +struct policy_mgr_conc_connection_info { + enum policy_mgr_con_mode mode; + uint8_t chan; + enum hw_mode_bandwidth bw; + uint8_t mac; + enum policy_mgr_chain_mode chain_mask; + uint32_t original_nss; + uint32_t vdev_id; + bool in_use; +}; + +/** + * struct policy_mgr_hw_mode_params - HW mode params + * @mac0_tx_ss: MAC0 Tx spatial stream + * @mac0_rx_ss: MAC0 Rx spatial stream + * @mac1_tx_ss: MAC1 Tx spatial stream + * @mac1_rx_ss: MAC1 Rx spatial stream + * @mac0_bw: MAC0 bandwidth + * @mac1_bw: MAC1 bandwidth + * @mac0_band_cap: mac0 band (5g/2g) capability + * @dbs_cap: DBS capabality + * @agile_dfs_cap: Agile DFS capabality + * @action_type: for dbs mode, the field indicates the "Action type" to be + * used to switch to the mode. To help the hw mode validation. + */ +struct policy_mgr_hw_mode_params { + uint8_t mac0_tx_ss; + uint8_t mac0_rx_ss; + uint8_t mac1_tx_ss; + uint8_t mac1_rx_ss; + uint8_t mac0_bw; + uint8_t mac1_bw; + uint8_t mac0_band_cap; + uint8_t dbs_cap; + uint8_t agile_dfs_cap; + uint8_t sbs_cap; + enum policy_mgr_conc_next_action action_type; +}; + +/** + * struct policy_mgr_vdev_mac_map - vdev id-mac id map + * @vdev_id: VDEV id + * @mac_id: MAC id + */ +struct policy_mgr_vdev_mac_map { + uint32_t vdev_id; + uint32_t mac_id; +}; + +/** + * struct policy_mgr_dual_mac_config - Dual MAC configuration + * @scan_config: Scan configuration + * @fw_mode_config: FW mode configuration + * @set_dual_mac_cb: Callback function to be executed on response to the command + */ +struct policy_mgr_dual_mac_config { + uint32_t scan_config; + uint32_t fw_mode_config; + dual_mac_cb set_dual_mac_cb; +}; + +/** + * struct policy_mgr_hw_mode - Format of set HW mode + * @hw_mode_index: Index of HW mode to be set + * @set_hw_mode_cb: HDD set HW mode callback + * @reason: Reason for HW mode change + * @session_id: Session id + * @next_action: next action to happen at policy mgr + * @context: psoc context + */ +struct policy_mgr_hw_mode { + uint32_t hw_mode_index; + void *set_hw_mode_cb; + enum policy_mgr_conn_update_reason reason; + uint32_t session_id; + uint8_t next_action; + struct wlan_objmgr_psoc *context; +}; + +/** + * struct policy_mgr_pcl_list - Format of PCL + * @pcl_list: List of preferred channels + * @weight_list: Weights of the PCL + * @pcl_len: Number of channels in the PCL + */ +struct policy_mgr_pcl_list { + uint8_t pcl_list[POLICY_MGR_MAX_CHANNEL_LIST]; + uint8_t weight_list[POLICY_MGR_MAX_CHANNEL_LIST]; + uint32_t pcl_len; +}; + +/** + * struct policy_mgr_pcl_chan_weights - Params to get the valid weighed list + * @pcl_list: Preferred channel list already sorted in the order of preference + * @pcl_len: Length of the PCL + * @saved_chan_list: Valid channel list updated as part of + * WMA_UPDATE_CHAN_LIST_REQ + * @saved_num_chan: Length of the valid channel list + * @weighed_valid_list: Weights of the valid channel list. This will have one + * to one mapping with valid_chan_list. FW expects channel order and size to be + * as per the list provided in WMI_SCAN_CHAN_LIST_CMDID. + * @weight_list: Weights assigned by policy manager + */ +struct policy_mgr_pcl_chan_weights { + uint8_t pcl_list[POLICY_MGR_MAX_CHANNEL_LIST]; + uint32_t pcl_len; + uint8_t saved_chan_list[POLICY_MGR_MAX_CHANNEL_LIST]; + uint32_t saved_num_chan; + uint8_t weighed_valid_list[POLICY_MGR_MAX_CHANNEL_LIST]; + uint8_t weight_list[POLICY_MGR_MAX_CHANNEL_LIST]; +}; + +/** + * struct policy_mgr_vdev_entry_info - vdev related param to be + * used by policy manager + * @type: type + * @sub_type: sub type + * @mhz: channel frequency in MHz + * @chan_width: channel bandwidth + * @mac_id: the mac on which vdev is on + */ +struct policy_mgr_vdev_entry_info { + uint32_t type; + uint32_t sub_type; + uint32_t mhz; + uint32_t chan_width; + uint32_t mac_id; +}; + +/** + * struct dbs_hw_mode_info - WLAN_DBS_HW_MODES_TLV Format + * @tlv_header: TLV header, TLV tag and len; tag equals WMITLV_TAG_ARRAY_UINT32 + * @hw_mode_list: WLAN_DBS_HW_MODE_LIST entries + */ +struct dbs_hw_mode_info { + uint32_t tlv_header; + uint32_t *hw_mode_list; +}; + +/** + * struct dual_mac_config - Dual MAC configurations + * @prev_scan_config: Previous scan configuration + * @prev_fw_mode_config: Previous FW mode configuration + * @cur_scan_config: Current scan configuration + * @cur_fw_mode_config: Current FW mode configuration + * @req_scan_config: Requested scan configuration + * @req_fw_mode_config: Requested FW mode configuration + */ +struct dual_mac_config { + uint32_t prev_scan_config; + uint32_t prev_fw_mode_config; + uint32_t cur_scan_config; + uint32_t cur_fw_mode_config; + uint32_t req_scan_config; + uint32_t req_fw_mode_config; +}; + +/** + * enum policy_mgr_pri_id - vdev type priority id + * @PM_STA_PRI_ID: station vdev type priority id + * @PM_SAP_PRI_ID: sap vdev type priority id + * @PM_P2P_GO_PRI_ID: p2p go vdev type priority id + * @PM_P2P_CLI_PRI_ID: p2p cli vdev type priority id + * @PM_MAX_PRI_ID: vdev type priority id max value + */ +enum policy_mgr_pri_id { + PM_STA_PRI_ID = 1, + PM_SAP_PRI_ID, + PM_P2P_GO_PRI_ID, + PM_P2P_CLI_PRI_ID, + PM_MAX_PRI_ID = 0xF, +}; + +#define PM_GET_BAND_PREFERRED(_policy_) ((_policy_) & 0x1) +#define PM_GET_VDEV_PRIORITY_ENABLED(_policy_) (((_policy_) & 0x2) >> 1) + +/** + * struct policy_mgr_user_cfg - Policy manager user config variables + * @enable2x2: 2x2 chain mask user config + * @sub_20_mhz_enabled: Is 5 or 10 Mhz enabled + */ +struct policy_mgr_user_cfg { + bool enable2x2; + bool sub_20_mhz_enabled; + bool is_sta_sap_scc_allowed_on_dfs_chan; + uint32_t sta_sap_scc_on_lte_coex_chan; +}; + +/** + * struct dbs_nss - Number of spatial streams in DBS mode + * @mac0_ss: Number of spatial streams on MAC0 + * @mac1_ss: Number of spatial streams on MAC1 + */ +struct dbs_nss { + enum hw_mode_ss_config mac0_ss; + enum hw_mode_ss_config mac1_ss; +}; + +/** + * struct connection_info - connection information + * @mac_id: The HW mac it is running + * @vdev_id: vdev id + * @channel: channel of the connection + */ +struct connection_info { + uint8_t mac_id; + uint8_t vdev_id; + uint8_t channel; +}; +#endif /* __WLAN_POLICY_MGR_PUBLIC_STRUCT_H */ diff --git a/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_ucfg.h b/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_ucfg.h new file mode 100644 index 000000000000..661eebdec81e --- /dev/null +++ b/components/cmn_services/policy_mgr/inc/wlan_policy_mgr_ucfg.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __WLAN_POLICY_MGR_UCFG +#define __WLAN_POLICY_MGR_UCFG +#include "wlan_objmgr_psoc_obj.h" +#include "wlan_objmgr_global_obj.h" +#include "qdf_status.h" + + +/** + * ucfg_policy_mgr_psoc_open() - This API sets CFGs to policy manager context + * + * This API pulls policy manager's context from PSOC and initialize the CFG + * structure of policy manager. + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_psoc_open(struct wlan_objmgr_psoc *psoc); +/** + * ucfg_policy_mgr_psoc_close() - This API resets CFGs for policy manager ctx + * + * This API pulls policy manager's context from PSOC and resets the CFG + * structure of policy manager. + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +void ucfg_policy_mgr_psoc_close(struct wlan_objmgr_psoc *psoc); +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +/** + * ucfg_policy_mgr_get_mcc_scc_switch() - To mcc to scc switch setting from INI + * @psoc: pointer to psoc + * @mcc_scc_switch: value to be filled + * + * This API pulls mcc to scc switch setting which is given as part of INI and + * stored in policy manager's CFGs. + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_get_mcc_scc_switch(struct wlan_objmgr_psoc *psoc, + uint8_t *mcc_scc_switch); +#else +static inline +QDF_STATUS ucfg_policy_mgr_get_mcc_scc_switch(struct wlan_objmgr_psoc *psoc, + uint8_t *mcc_scc_switch) +{ + return QDF_STATUS_SUCCESS; +} +#endif //FEATURE_WLAN_MCC_TO_SCC_SWITCH +/** + * ucfg_policy_mgr_get_sys_pref() - to get system preference + * @psoc: pointer to psoc + * @sys_pref: value to be filled + * + * This API pulls the system preference for policy manager to provide + * PCL + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_get_sys_pref(struct wlan_objmgr_psoc *psoc, + uint8_t *sys_pref); +/** + * ucfg_policy_mgr_set_sys_pref() - to set system preference + * @psoc: pointer to psoc + * @sys_pref: value to be applied as new INI setting + * + * This API is meant to override original INI setting for system pref + * with new value which is used by policy manager to provide PCL + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_set_sys_pref(struct wlan_objmgr_psoc *psoc, + uint8_t sys_pref); +/** + * ucfg_policy_mgr_get_max_conc_cxns() - to get max num of conc connections + * @psoc: pointer to psoc + * @max_conc_cxns: value to be filled + * + * This API pulls max number of active connections which can be allowed + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_get_max_conc_cxns(struct wlan_objmgr_psoc *psoc, + uint8_t *max_conc_cxns); +/** + * ucfg_policy_mgr_get_conc_rule1() - to find out if conc rule1 is enabled + * @psoc: pointer to psoc + * @conc_rule1: value to be filled + * + * This API is used to find out if conc rule-1 is enabled by user + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_get_conc_rule1(struct wlan_objmgr_psoc *psoc, + uint8_t *conc_rule1); +/** + * ucfg_policy_mgr_get_conc_rule2() - to find out if conc rule2 is enabled + * @psoc: pointer to psoc + * @conc_rule2: value to be filled + * + * This API is used to find out if conc rule-2 is enabled by user + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_get_conc_rule2(struct wlan_objmgr_psoc *psoc, + uint8_t *conc_rule2); +/** + * ucfg_policy_mgr_get_dbs_selection_plcy() - DBS HW mode selection setting + * @psoc: pointer to psoc + * @dbs_selection_plcy: value to be filled + * + * This API is used to find out DBS HW mode preference. + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_get_dbs_selection_plcy(struct wlan_objmgr_psoc *psoc, + uint32_t *dbs_selection_plcy); +/** + * ucfg_policy_mgr_get_vdev_priority_list() - to get vdev priority list + * @psoc: pointer to psoc + * @vdev_priority_list: value to be filled + * + * This API is used to find out vdev_priority_list setting + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_get_vdev_priority_list(struct wlan_objmgr_psoc *psoc, + uint32_t *vdev_priority_list); +/** + * policy_mgr_get_chnl_select_plcy() - to get channel selection policy + * @psoc: pointer to psoc + * @chnl_select_plcy: value to be filled + * + * This API is used to find out which channel selection policy has been + * configured + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_get_chnl_select_plcy(struct wlan_objmgr_psoc *psoc, + uint32_t *chnl_select_plcy); +/** + * policy_mgr_get_mcc_adaptive_sch() - to get mcc adaptive scheduler + * @psoc: pointer to psoc + * @enable_mcc_adaptive_sch: value to be filled + * + * This API is used to find out if mcc adaptive scheduler enabled or disabled + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS +ucfg_policy_mgr_get_mcc_adaptive_sch(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_mcc_adaptive_sch); + +/** + * ucfg_policy_mgr_get_sta_cxn_5g_band() - to get STA's connection in 5G config + * + * @psoc: pointer to psoc + * @enable_sta_cxn_5g_band: value to be filled + * + * This API is used to find out if STA connection in 5G band is allowed or + * disallowed. + * + * Return: QDF_STATUS_SUCCESS up on success and any other status for failure. + */ +QDF_STATUS ucfg_policy_mgr_get_sta_cxn_5g_band(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_sta_cxn_5g_band); +#endif //__WLAN_POLICY_MGR_UCFG diff --git a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_action.c b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_action.c new file mode 100644 index 000000000000..809406cc8191 --- /dev/null +++ b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_action.c @@ -0,0 +1,2183 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_policy_mgr_action.c + * + * WLAN Concurrenct Connection Management APIs + * + */ + +/* Include files */ + +#include "wlan_policy_mgr_api.h" +#include "wlan_policy_mgr_tables_no_dbs_i.h" +#include "wlan_policy_mgr_i.h" +#include "qdf_types.h" +#include "qdf_trace.h" +#include "wlan_objmgr_global_obj.h" +#include "qdf_platform.h" + +enum policy_mgr_conc_next_action (*policy_mgr_get_current_pref_hw_mode_ptr) + (struct wlan_objmgr_psoc *psoc); + +void policy_mgr_hw_mode_transition_cb(uint32_t old_hw_mode_index, + uint32_t new_hw_mode_index, + uint32_t num_vdev_mac_entries, + struct policy_mgr_vdev_mac_map *vdev_mac_map, + struct wlan_objmgr_psoc *context) +{ + QDF_STATUS status; + struct policy_mgr_hw_mode_params hw_mode; + uint32_t i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(context); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return; + } + + if (!vdev_mac_map) { + policy_mgr_err("vdev_mac_map is NULL"); + return; + } + + policy_mgr_debug("old_hw_mode_index=%d, new_hw_mode_index=%d", + old_hw_mode_index, new_hw_mode_index); + + for (i = 0; i < num_vdev_mac_entries; i++) + policy_mgr_debug("vdev_id:%d mac_id:%d", + vdev_mac_map[i].vdev_id, + vdev_mac_map[i].mac_id); + + status = policy_mgr_get_hw_mode_from_idx(context, + new_hw_mode_index, &hw_mode); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Get HW mode failed: %d", status); + return; + } + + policy_mgr_debug("MAC0: TxSS:%d, RxSS:%d, Bw:%d band_cap:%d", + hw_mode.mac0_tx_ss, hw_mode.mac0_rx_ss, + hw_mode.mac0_bw, hw_mode.mac0_band_cap); + policy_mgr_debug("MAC1: TxSS:%d, RxSS:%d, Bw:%d", + hw_mode.mac1_tx_ss, hw_mode.mac1_rx_ss, + hw_mode.mac1_bw); + policy_mgr_debug("DBS:%d, Agile DFS:%d, SBS:%d", + hw_mode.dbs_cap, hw_mode.agile_dfs_cap, + hw_mode.sbs_cap); + + /* update pm_conc_connection_list */ + policy_mgr_update_hw_mode_conn_info(context, num_vdev_mac_entries, + vdev_mac_map, + hw_mode); + + if (pm_ctx->mode_change_cb) + pm_ctx->mode_change_cb(); + + return; +} + +QDF_STATUS policy_mgr_check_n_start_opportunistic_timer( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("PM ctx not valid. Oppurtunistic timer cannot start"); + return QDF_STATUS_E_FAILURE; + } + if (policy_mgr_need_opportunistic_upgrade(psoc, NULL)) { + /* let's start the timer */ + qdf_mc_timer_stop(&pm_ctx->dbs_opportunistic_timer); + status = qdf_mc_timer_start( + &pm_ctx->dbs_opportunistic_timer, + DBS_OPPORTUNISTIC_TIME * 1000); + if (!QDF_IS_STATUS_SUCCESS(status)) + policy_mgr_err("Failed to start dbs opportunistic timer"); + } + return status; +} + +QDF_STATUS policy_mgr_pdev_set_hw_mode(struct wlan_objmgr_psoc *psoc, + uint32_t session_id, + enum hw_mode_ss_config mac0_ss, + enum hw_mode_bandwidth mac0_bw, + enum hw_mode_ss_config mac1_ss, + enum hw_mode_bandwidth mac1_bw, + enum hw_mode_mac_band_cap mac0_band_cap, + enum hw_mode_dbs_capab dbs, + enum hw_mode_agile_dfs_capab dfs, + enum hw_mode_sbs_capab sbs, + enum policy_mgr_conn_update_reason reason, + uint8_t next_action) +{ + int8_t hw_mode_index; + struct policy_mgr_hw_mode msg; + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + /* + * if HW is not capable of doing 2x2 or ini config disabled 2x2, don't + * allow to request FW for 2x2 + */ + if ((HW_MODE_SS_2x2 == mac0_ss) && (!pm_ctx->user_cfg.enable2x2)) { + policy_mgr_debug("2x2 is not allowed downgrading to 1x1 for mac0"); + mac0_ss = HW_MODE_SS_1x1; + } + if ((HW_MODE_SS_2x2 == mac1_ss) && (!pm_ctx->user_cfg.enable2x2)) { + policy_mgr_debug("2x2 is not allowed downgrading to 1x1 for mac1"); + mac1_ss = HW_MODE_SS_1x1; + } + + hw_mode_index = policy_mgr_get_hw_mode_idx_from_dbs_hw_list(psoc, + mac0_ss, mac0_bw, mac1_ss, mac1_bw, mac0_band_cap, + dbs, dfs, sbs); + if (hw_mode_index < 0) { + policy_mgr_err("Invalid HW mode index obtained"); + return QDF_STATUS_E_FAILURE; + } + + msg.hw_mode_index = hw_mode_index; + msg.set_hw_mode_cb = (void *)policy_mgr_pdev_set_hw_mode_cb; + msg.reason = reason; + msg.session_id = session_id; + msg.next_action = next_action; + msg.context = psoc; + + policy_mgr_debug("set hw mode to sme: hw_mode_index: %d session:%d reason:%d", + msg.hw_mode_index, msg.session_id, msg.reason); + + status = pm_ctx->sme_cbacks.sme_pdev_set_hw_mode(msg); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to set hw mode to SME"); + return status; + } + + return QDF_STATUS_SUCCESS; +} + +enum policy_mgr_conc_next_action policy_mgr_need_opportunistic_upgrade( + struct wlan_objmgr_psoc *psoc, + enum policy_mgr_conn_update_reason *reason) +{ + uint32_t conn_index; + enum policy_mgr_conc_next_action upgrade = PM_NOP; + enum policy_mgr_conc_next_action preferred_dbs_action; + uint8_t mac = 0; + struct policy_mgr_hw_mode_params hw_mode; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + goto exit; + } + + if (policy_mgr_is_hw_dbs_capable(psoc) == false) { + policy_mgr_err("driver isn't dbs capable, no further action needed"); + goto exit; + } + + status = policy_mgr_get_current_hw_mode(psoc, &hw_mode); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("policy_mgr_get_current_hw_mode failed"); + goto exit; + } + if (!hw_mode.dbs_cap) { + policy_mgr_debug("current HW mode is non-DBS capable"); + goto exit; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + /* Are both mac's still in use */ + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + policy_mgr_debug("index:%d mac:%d in_use:%d chan:%d org_nss:%d", + conn_index, + pm_conc_connection_list[conn_index].mac, + pm_conc_connection_list[conn_index].in_use, + pm_conc_connection_list[conn_index].chan, + pm_conc_connection_list[conn_index].original_nss); + if ((pm_conc_connection_list[conn_index].mac == 0) && + pm_conc_connection_list[conn_index].in_use) { + mac |= POLICY_MGR_MAC0; + if (POLICY_MGR_MAC0_AND_MAC1 == mac) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + goto done; + } + } else if ((pm_conc_connection_list[conn_index].mac == 1) && + pm_conc_connection_list[conn_index].in_use) { + mac |= POLICY_MGR_MAC1; + if (policy_mgr_is_hw_dbs_2x2_capable(psoc) && + WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[conn_index].chan) + ) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_debug("2X2 DBS capable with 2.4 GHZ connection"); + goto done; + } + if (POLICY_MGR_MAC0_AND_MAC1 == mac) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + goto done; + } + } + } + /* Let's request for single MAC mode */ + upgrade = PM_SINGLE_MAC; + if (reason) + *reason = POLICY_MGR_UPDATE_REASON_OPPORTUNISTIC; + /* Is there any connection had an initial connection with 2x2 */ + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if ((pm_conc_connection_list[conn_index].original_nss == 2) && + pm_conc_connection_list[conn_index].in_use) { + upgrade = PM_SINGLE_MAC_UPGRADE; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + goto done; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + +done: + if (upgrade == PM_NOP && hw_mode.dbs_cap && + policy_mgr_is_2x2_1x1_dbs_capable(psoc)) { + preferred_dbs_action = + policy_mgr_get_preferred_dbs_action_table( + psoc, INVALID_VDEV_ID, 0, 0); + if (hw_mode.action_type == PM_DBS1 && + preferred_dbs_action == PM_DBS2) { + upgrade = PM_DBS2_DOWNGRADE; + if (reason) + *reason = + POLICY_MGR_UPDATE_REASON_PRI_VDEV_CHANGE; + } else if (hw_mode.action_type == PM_DBS2 && + preferred_dbs_action == PM_DBS1) { + upgrade = PM_DBS1_DOWNGRADE; + if (reason) + *reason = + POLICY_MGR_UPDATE_REASON_PRI_VDEV_CHANGE; + } + } +exit: + return upgrade; +} + +QDF_STATUS policy_mgr_update_connection_info(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t conn_index = 0; + bool found = false; + struct policy_mgr_vdev_entry_info conn_table_entry; + enum policy_mgr_chain_mode chain_mask = POLICY_MGR_ONE_ONE; + uint8_t nss_2g, nss_5g; + enum policy_mgr_con_mode mode; + uint8_t chan; + uint32_t nss = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return status; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (vdev_id == pm_conc_connection_list[conn_index].vdev_id) { + /* debug msg */ + found = true; + break; + } + conn_index++; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + if (!found) { + /* err msg */ + policy_mgr_err("can't find vdev_id %d in pm_conc_connection_list", + vdev_id); + return status; + } + if (pm_ctx->wma_cbacks.wma_get_connection_info) { + status = pm_ctx->wma_cbacks.wma_get_connection_info( + vdev_id, &conn_table_entry); + if (QDF_STATUS_SUCCESS != status) { + policy_mgr_err("can't find vdev_id %d in connection table", + vdev_id); + return status; + } + } else { + policy_mgr_err("wma_get_connection_info is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mode = policy_mgr_get_mode(conn_table_entry.type, + conn_table_entry.sub_type); + chan = wlan_reg_freq_to_chan(pm_ctx->pdev, conn_table_entry.mhz); + status = policy_mgr_get_nss_for_vdev(psoc, mode, &nss_2g, &nss_5g); + if (QDF_IS_STATUS_SUCCESS(status)) { + if ((WLAN_REG_IS_24GHZ_CH(chan) && (nss_2g > 1)) || + (WLAN_REG_IS_5GHZ_CH(chan) && (nss_5g > 1))) + chain_mask = POLICY_MGR_TWO_TWO; + else + chain_mask = POLICY_MGR_ONE_ONE; + nss = (WLAN_REG_IS_24GHZ_CH(chan)) ? nss_2g : nss_5g; + } else { + policy_mgr_err("Error in getting nss"); + } + + policy_mgr_debug("update PM connection table for vdev:%d", vdev_id); + + /* add the entry */ + policy_mgr_update_conc_list(psoc, conn_index, + mode, + chan, + policy_mgr_get_bw(conn_table_entry.chan_width), + conn_table_entry.mac_id, + chain_mask, + nss, vdev_id, true, true); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_update_and_wait_for_connection_update( + struct wlan_objmgr_psoc *psoc, + uint8_t session_id, + uint8_t channel, + enum policy_mgr_conn_update_reason reason) +{ + QDF_STATUS status; + + policy_mgr_debug("session:%d channel:%d reason:%d", + session_id, channel, reason); + + status = policy_mgr_reset_connection_update(psoc); + if (QDF_IS_STATUS_ERROR(status)) + policy_mgr_err("clearing event failed"); + + status = policy_mgr_current_connections_update(psoc, + session_id, channel, reason); + if (QDF_STATUS_E_FAILURE == status) { + policy_mgr_err("connections update failed"); + return QDF_STATUS_E_FAILURE; + } + + /* Wait only when status is success */ + if (QDF_IS_STATUS_SUCCESS(status)) { + status = policy_mgr_wait_for_connection_update(psoc); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("qdf wait for event failed"); + return QDF_STATUS_E_FAILURE; + } + } + + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_is_dbs_allowed_for_concurrency() - If dbs is allowed for current + * concurreny + * @new_conn_mode: new connection mode + * + * When a new connection is about to come up, check if dbs is allowed for + * STA+STA or STA+P2P + * + * Return: true if dbs is allowed for STA+STA or STA+P2P else false + */ +bool policy_mgr_is_dbs_allowed_for_concurrency( + struct wlan_objmgr_psoc *psoc, enum QDF_OPMODE new_conn_mode) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t count, dbs_for_sta_sta, dbs_for_sta_p2p; + bool ret = true; + uint32_t ch_sel_plcy; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return ret; + } + + count = policy_mgr_get_connection_count(psoc); + + if (count != 1 || new_conn_mode == QDF_MAX_NO_OF_MODE) + return ret; + + ch_sel_plcy = pm_ctx->cfg.chnl_select_plcy; + dbs_for_sta_sta = PM_CHANNEL_SELECT_LOGIC_STA_STA_GET(ch_sel_plcy); + dbs_for_sta_p2p = PM_CHANNEL_SELECT_LOGIC_STA_P2P_GET(ch_sel_plcy); + + switch (pm_conc_connection_list[0].mode) { + case PM_STA_MODE: + switch (new_conn_mode) { + case QDF_STA_MODE: + if (!dbs_for_sta_sta) + return false; + break; + case QDF_P2P_DEVICE_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_P2P_GO_MODE: + if (!dbs_for_sta_p2p) + return false; + break; + default: + break; + } + break; + case PM_P2P_CLIENT_MODE: + case PM_P2P_GO_MODE: + switch (new_conn_mode) { + case QDF_STA_MODE: + if (!dbs_for_sta_p2p) + return false; + break; + default: + break; + } + break; + default: + break; + } + + return ret; +} + +bool policy_mgr_is_chnl_in_diff_band(struct wlan_objmgr_psoc *psoc, + uint8_t channel) +{ + uint8_t i, pm_chnl; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + /* + * check given channel against already existing connections' + * channels. if they differ then channels are in different bands + */ + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { + pm_chnl = pm_conc_connection_list[i].chan; + if (pm_conc_connection_list[i].in_use) + if (!WLAN_REG_IS_SAME_BAND_CHANNELS(channel, pm_chnl)) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_debug("channel is in diff band"); + return true; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return false; +} + +bool policy_mgr_is_hwmode_set_for_given_chnl(struct wlan_objmgr_psoc *psoc, + uint8_t channel) +{ + enum policy_mgr_band band; + bool is_hwmode_dbs, is_2x2_dbs; + + if (policy_mgr_is_hw_dbs_capable(psoc) == false) + return true; + + if (WLAN_REG_IS_24GHZ_CH(channel)) + band = POLICY_MGR_BAND_24; + else + band = POLICY_MGR_BAND_5; + + is_hwmode_dbs = policy_mgr_is_current_hwmode_dbs(psoc); + is_2x2_dbs = policy_mgr_is_hw_dbs_2x2_capable(psoc); + /* + * If HW supports 2x2 chains in DBS HW mode and if DBS HW mode is not + * yet set then this is the right time to block the connection. + */ + if ((band == POLICY_MGR_BAND_24) && is_2x2_dbs && !is_hwmode_dbs) { + policy_mgr_err("HW mode is not yet in DBS!!!!!"); + return false; + } + + /* + * If HW supports 1x1 chains DBS HW mode and if first connection is + * 2G or 5G band and if second connection is coming up in diffrent + * band than the first connection and if current HW mode is not yet + * set in DBS then this is the right time to block the connection. + */ + if (policy_mgr_is_chnl_in_diff_band(psoc, channel) && !is_hwmode_dbs) { + policy_mgr_err("Given channel & existing conn is diff band & HW mode is not yet in DBS !!!!"); + return false; + } + + return true; +} + +/** + * policy_mgr_pri_id_to_con_mode() - convert policy_mgr_pri_id to + * policy_mgr_con_mode + * @pri_id: policy_mgr_pri_id + * + * The help function converts policy_mgr_pri_id type to policy_mgr_con_mode + * type. + * + * Return: policy_mgr_con_mode type. + */ +static +enum policy_mgr_con_mode policy_mgr_pri_id_to_con_mode( + enum policy_mgr_pri_id pri_id) +{ + switch (pri_id) { + case PM_STA_PRI_ID: + return PM_STA_MODE; + case PM_SAP_PRI_ID: + return PM_SAP_MODE; + case PM_P2P_GO_PRI_ID: + return PM_P2P_GO_MODE; + case PM_P2P_CLI_PRI_ID: + return PM_P2P_CLIENT_MODE; + default: + return PM_MAX_NUM_OF_MODE; + } +} + +enum policy_mgr_conc_next_action +policy_mgr_get_preferred_dbs_action_table( + struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + uint8_t channel, + enum policy_mgr_conn_update_reason reason) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + enum policy_mgr_con_mode pri_conn_mode = PM_MAX_NUM_OF_MODE; + enum policy_mgr_con_mode new_conn_mode = PM_MAX_NUM_OF_MODE; + enum QDF_OPMODE new_conn_op_mode = QDF_MAX_NO_OF_MODE; + bool band_pref_5g = true; + bool vdev_priority_enabled = false; + bool dbs_2x2_5g_1x1_2g_supported; + bool dbs_2x2_2g_1x1_5g_supported; + uint32_t vdev_pri_list, vdev_pri_id; + uint8_t chan_list[MAX_NUMBER_OF_CONC_CONNECTIONS + 1]; + uint8_t vdev_list[MAX_NUMBER_OF_CONC_CONNECTIONS + 1]; + uint32_t vdev_count = 0; + uint32_t i; + bool found; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return PM_NOP; + } + dbs_2x2_5g_1x1_2g_supported = + policy_mgr_is_2x2_5G_1x1_2G_dbs_capable(psoc); + dbs_2x2_2g_1x1_5g_supported = + policy_mgr_is_2x2_2G_1x1_5G_dbs_capable(psoc); + policy_mgr_debug("target support DBS1 %d DBS2 %d", + dbs_2x2_5g_1x1_2g_supported, + dbs_2x2_2g_1x1_5g_supported); + /* + * If both DBS1 and DBS2 not supported, this should be Legacy Single + * DBS mode HW. The policy_mgr_psoc_enable has setup the correct + * action tables. + */ + if (!dbs_2x2_5g_1x1_2g_supported && !dbs_2x2_2g_1x1_5g_supported) + return PM_NOP; + if (!dbs_2x2_5g_1x1_2g_supported) { + band_pref_5g = false; + policy_mgr_debug("target only supports DBS2!"); + goto DONE; + } + if (!dbs_2x2_2g_1x1_5g_supported) { + policy_mgr_debug("target only supports DBS1!"); + goto DONE; + } + if (PM_GET_BAND_PREFERRED(pm_ctx->cfg.dbs_selection_plcy) == 1) + band_pref_5g = false; + + if (PM_GET_VDEV_PRIORITY_ENABLED( + pm_ctx->cfg.dbs_selection_plcy) == 1 && + pm_ctx->cfg.vdev_priority_list) + vdev_priority_enabled = true; + + if (!vdev_priority_enabled) + goto DONE; + + if (vdev_id != INVALID_VDEV_ID && channel) { + if (pm_ctx->hdd_cbacks.hdd_get_device_mode) + new_conn_op_mode = pm_ctx->hdd_cbacks. + hdd_get_device_mode(vdev_id); + + new_conn_mode = policy_mgr_convert_device_mode_to_qdf_type( + new_conn_op_mode); + if (new_conn_mode == PM_MAX_NUM_OF_MODE) + policy_mgr_debug("new vdev %d op_mode %d chan %d reason %d: not prioritized", + vdev_id, new_conn_op_mode, + channel, reason); + else + policy_mgr_debug("new vdev %d op_mode %d chan %d : reason %d", + vdev_id, new_conn_op_mode, channel, + reason); + } + vdev_pri_list = pm_ctx->cfg.vdev_priority_list; + while (vdev_pri_list) { + vdev_pri_id = vdev_pri_list & 0xF; + pri_conn_mode = policy_mgr_pri_id_to_con_mode(vdev_pri_id); + if (pri_conn_mode == PM_MAX_NUM_OF_MODE) { + policy_mgr_debug("vdev_pri_id %d prioritization not supported", + vdev_pri_id); + goto NEXT; + } + vdev_count = policy_mgr_get_mode_specific_conn_info( + psoc, chan_list, vdev_list, pri_conn_mode); + /** + * Take care of duplication case, the vdev id may + * exist in the conn list already with old chan. + * Replace with new chan before make decision. + */ + found = false; + for (i = 0; i < vdev_count; i++) { + policy_mgr_debug("[%d] vdev %d chan %d conn_mode %d", + i, vdev_list[i], chan_list[i], + pri_conn_mode); + + if (new_conn_mode == pri_conn_mode && + vdev_list[i] == vdev_id) { + chan_list[i] = channel; + found = true; + } + } + /** + * The new coming vdev should be added to the list to + * make decision if it is prioritized. + */ + if (!found && new_conn_mode == pri_conn_mode) { + chan_list[vdev_count] = channel; + vdev_list[vdev_count++] = vdev_id; + } + /** + * if more than one vdev has same priority, keep "band_pref_5g" + * value as default band preference setting. + */ + if (vdev_count > 1) + break; + /** + * select the only active vdev (or new coming vdev) chan as + * preferred band. + */ + if (vdev_count > 0) { + band_pref_5g = WLAN_REG_IS_5GHZ_CH(chan_list[0]); + break; + } +NEXT: + vdev_pri_list >>= 4; + } +DONE: + policy_mgr_debug("band_pref_5g %d", band_pref_5g); + if (band_pref_5g) + return PM_DBS1; + else + return PM_DBS2; +} + +/** + * policy_mgr_get_second_conn_action_table() - get second conn action table + * @psoc: Pointer to psoc + * @vdev_id: vdev Id + * @channel: channel of vdev. + * @reason: reason of request + * + * Get the action table based on current HW Caps and INI user preference. + * This function will be called by policy_mgr_current_connections_update during + * DBS action decision. + * + * return : action table address + */ +static policy_mgr_next_action_two_connection_table_type * +policy_mgr_get_second_conn_action_table( + struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + uint8_t channel, + enum policy_mgr_conn_update_reason reason) +{ + enum policy_mgr_conc_next_action preferred_action; + + if (!policy_mgr_is_2x2_1x1_dbs_capable(psoc)) + return next_action_two_connection_table; + + preferred_action = policy_mgr_get_preferred_dbs_action_table( + psoc, vdev_id, channel, reason); + switch (preferred_action) { + case PM_DBS2: + return next_action_two_connection_2x2_2g_1x1_5g_table; + default: + return next_action_two_connection_table; + } +} + +/** + * policy_mgr_get_third_conn_action_table() - get third connection action table + * @psoc: Pointer to psoc + * @vdev_id: vdev Id + * @channel: channel of vdev. + * @reason: reason of request + * + * Get the action table based on current HW Caps and INI user preference. + * This function will be called by policy_mgr_current_connections_update during + * DBS action decision. + * + * return : action table address + */ +static policy_mgr_next_action_three_connection_table_type * +policy_mgr_get_third_conn_action_table( + struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + uint8_t channel, + enum policy_mgr_conn_update_reason reason) +{ + enum policy_mgr_conc_next_action preferred_action; + + if (!policy_mgr_is_2x2_1x1_dbs_capable(psoc)) + return next_action_three_connection_table; + + preferred_action = policy_mgr_get_preferred_dbs_action_table( + psoc, vdev_id, channel, reason); + switch (preferred_action) { + case PM_DBS2: + return next_action_three_connection_2x2_2g_1x1_5g_table; + default: + return next_action_three_connection_table; + } +} + +QDF_STATUS policy_mgr_current_connections_update(struct wlan_objmgr_psoc *psoc, + uint32_t session_id, + uint8_t channel, + enum policy_mgr_conn_update_reason reason) +{ + enum policy_mgr_conc_next_action next_action = PM_NOP; + uint32_t num_connections = 0; + enum policy_mgr_one_connection_mode second_index = 0; + enum policy_mgr_two_connection_mode third_index = 0; + policy_mgr_next_action_two_connection_table_type *second_conn_table; + policy_mgr_next_action_three_connection_table_type *third_conn_table; + enum policy_mgr_band band; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct policy_mgr_psoc_priv_obj *pm_ctx; + enum QDF_OPMODE new_conn_mode = QDF_MAX_NO_OF_MODE; + + if (policy_mgr_is_hw_dbs_capable(psoc) == false) { + policy_mgr_err("driver isn't dbs capable, no further action needed"); + return QDF_STATUS_E_NOSUPPORT; + } + if (WLAN_REG_IS_24GHZ_CH(channel)) + band = POLICY_MGR_BAND_24; + else + band = POLICY_MGR_BAND_5; + + num_connections = policy_mgr_get_connection_count(psoc); + + policy_mgr_debug("num_connections=%d channel=%d", + num_connections, channel); + + switch (num_connections) { + case 0: + if (band == POLICY_MGR_BAND_24) + if (policy_mgr_is_hw_dbs_2x2_capable(psoc)) + next_action = PM_DBS; + else + next_action = PM_NOP; + else + next_action = PM_NOP; + break; + case 1: + second_index = + policy_mgr_get_second_connection_pcl_table_index(psoc); + if (PM_MAX_ONE_CONNECTION_MODE == second_index) { + policy_mgr_err( + "couldn't find index for 2nd connection next action table"); + goto done; + } + second_conn_table = policy_mgr_get_second_conn_action_table( + psoc, session_id, channel, reason); + next_action = (*second_conn_table)[second_index][band]; + break; + case 2: + third_index = + policy_mgr_get_third_connection_pcl_table_index(psoc); + if (PM_MAX_TWO_CONNECTION_MODE == third_index) { + policy_mgr_err( + "couldn't find index for 3rd connection next action table"); + goto done; + } + third_conn_table = policy_mgr_get_third_conn_action_table( + psoc, session_id, channel, reason); + next_action = (*third_conn_table)[third_index][band]; + break; + default: + policy_mgr_err("unexpected num_connections value %d", + num_connections); + break; + } + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + goto done; + } + + if (pm_ctx->hdd_cbacks.hdd_get_device_mode) + new_conn_mode = pm_ctx->hdd_cbacks. + hdd_get_device_mode(session_id); + + /* + * Based on channel_select_logic_conc ini, hw mode is set + * when second connection is about to come up that results + * in STA+STA and STA+P2P concurrency. + * 1) If MCC is set and if current hw mode is dbs, hw mode + * should be set to single mac for above concurrency. + * 2) If MCC is set and if current hw mode is not dbs, hw + * mode change is not required. + */ + if (policy_mgr_is_current_hwmode_dbs(psoc) && + !policy_mgr_is_dbs_allowed_for_concurrency(psoc, new_conn_mode)) + next_action = PM_SINGLE_MAC; + else if (!policy_mgr_is_current_hwmode_dbs(psoc) && + !policy_mgr_is_dbs_allowed_for_concurrency(psoc, new_conn_mode)) + next_action = PM_NOP; + + if (PM_NOP != next_action) + status = policy_mgr_next_actions(psoc, session_id, + next_action, reason); + else + status = QDF_STATUS_E_NOSUPPORT; + + policy_mgr_debug( + "idx2=%d idx3=%d next_action=%d, band=%d status=%d reason=%d session_id=%d", + second_index, third_index, next_action, band, status, + reason, session_id); + +done: + return status; +} + +/** + * policy_mgr_validate_dbs_switch() - Check DBS action valid or not + * @psoc: Pointer to psoc + * @session_id: vdev id + * @action: action requested + * @reason: reason of hw mode change + * + * This routine will check the current hw mode with requested action. + * If we are already in the mode, the caller will do nothing. + * This will be called by policy_mgr_next_actions to check the action needed + * or not. + * + * return : QDF_STATUS_SUCCESS, action is allowed. + * QDF_STATUS_E_ALREADY, action is not needed. + * QDF_STATUS_E_FAILURE, error happens. + * QDF_STATUS_E_NOSUPPORT, the requested mode not supported. + */ +static +QDF_STATUS policy_mgr_validate_dbs_switch( + struct wlan_objmgr_psoc *psoc, + uint32_t session_id, + enum policy_mgr_conc_next_action action, + enum policy_mgr_conn_update_reason reason) +{ + QDF_STATUS status; + struct policy_mgr_hw_mode_params hw_mode; + + /* check for the current HW index to see if really need any action */ + status = policy_mgr_get_current_hw_mode(psoc, &hw_mode); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("policy_mgr_get_current_hw_mode failed"); + return status; + } + + if (hw_mode.sbs_cap) { + if ((action == PM_SBS) || (action == PM_SBS_DOWNGRADE)) { + if (!policy_mgr_is_hw_sbs_capable(psoc)) { + /* No action */ + policy_mgr_notice("firmware is not sbs capable"); + return QDF_STATUS_E_NOSUPPORT; + } + /* current mode is already SBS nothing to be + * done + */ + policy_mgr_notice("current mode is already SBS"); + return QDF_STATUS_E_ALREADY; + } else { + return QDF_STATUS_SUCCESS; + } + } + + if (!hw_mode.dbs_cap) { + if (action == PM_SINGLE_MAC || + action == PM_SINGLE_MAC_UPGRADE) { + policy_mgr_notice("current mode is already single MAC"); + return QDF_STATUS_E_ALREADY; + } else { + return QDF_STATUS_SUCCESS; + } + } + /** + * If already in DBS, no need to request DBS again (HL, Napier). + * For dual DBS HW, in case DBS1 -> DBS2 or DBS2 -> DBS1 + * switching, we need to check the current DBS mode is same as + * requested or not. + */ + if (policy_mgr_is_2x2_5G_1x1_2G_dbs_capable(psoc) || + policy_mgr_is_2x2_2G_1x1_5G_dbs_capable(psoc)) { + policy_mgr_info("curr dbs action %d new action %d", + hw_mode.action_type, action); + if (hw_mode.action_type == PM_DBS1 && + ((action == PM_DBS1 || + action == PM_DBS1_DOWNGRADE))) { + policy_mgr_err("driver is already in DBS_5G_2x2_24G_1x1 (%d), no further action %d needed", + hw_mode.action_type, action); + return QDF_STATUS_E_ALREADY; + } else if (hw_mode.action_type == PM_DBS2 && + ((action == PM_DBS2 || + action == PM_DBS2_DOWNGRADE))) { + policy_mgr_err("driver is already in DBS_24G_2x2_5G_1x1 (%d), no further action %d needed", + hw_mode.action_type, action); + return QDF_STATUS_E_ALREADY; + } + } else if ((action == PM_DBS_DOWNGRADE) || (action == PM_DBS) || + (action == PM_DBS_UPGRADE)) { + policy_mgr_err("driver is already in %s mode, no further action needed", + (hw_mode.dbs_cap) ? "dbs" : "non dbs"); + return QDF_STATUS_E_ALREADY; + } + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_next_actions( + struct wlan_objmgr_psoc *psoc, + uint32_t session_id, + enum policy_mgr_conc_next_action action, + enum policy_mgr_conn_update_reason reason) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct dbs_nss nss_dbs = {0}; + struct policy_mgr_hw_mode_params hw_mode; + enum policy_mgr_conc_next_action next_action; + + if (policy_mgr_is_hw_dbs_capable(psoc) == false) { + policy_mgr_err("driver isn't dbs capable, no further action needed"); + return QDF_STATUS_E_NOSUPPORT; + } + + /* check for the current HW index to see if really need any action */ + status = policy_mgr_get_current_hw_mode(psoc, &hw_mode); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("policy_mgr_get_current_hw_mode failed"); + return status; + } + + /* check for the current HW index to see if really need any action */ + status = policy_mgr_validate_dbs_switch(psoc, session_id, action, + reason); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err(" not take action %d reason %d session %d status %d", + action, reason, session_id, status); + return status; + } + + switch (action) { + case PM_DBS_DOWNGRADE: + /* + * check if we have a beaconing entity that is using 2x2. If yes, + * update the beacon template & notify FW. Once FW confirms + * beacon updated, send down the HW mode change req + */ + status = policy_mgr_complete_action(psoc, POLICY_MGR_RX_NSS_1, + PM_DBS, reason, session_id); + break; + case PM_DBS: + (void)policy_mgr_get_hw_dbs_nss(psoc, &nss_dbs); + + status = policy_mgr_pdev_set_hw_mode(psoc, session_id, + nss_dbs.mac0_ss, + HW_MODE_80_MHZ, + nss_dbs.mac1_ss, + HW_MODE_40_MHZ, + HW_MODE_MAC_BAND_NONE, + HW_MODE_DBS, + HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS_NONE, + reason, PM_NOP); + break; + case PM_SINGLE_MAC_UPGRADE: + /* + * change the HW mode first before the NSS upgrade + */ + status = policy_mgr_pdev_set_hw_mode(psoc, session_id, + HW_MODE_SS_2x2, + HW_MODE_80_MHZ, + HW_MODE_SS_0x0, HW_MODE_BW_NONE, + HW_MODE_MAC_BAND_NONE, + HW_MODE_DBS_NONE, + HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS_NONE, + reason, PM_UPGRADE); + break; + case PM_SINGLE_MAC: + status = policy_mgr_pdev_set_hw_mode(psoc, session_id, + HW_MODE_SS_2x2, + HW_MODE_80_MHZ, + HW_MODE_SS_0x0, HW_MODE_BW_NONE, + HW_MODE_MAC_BAND_NONE, + HW_MODE_DBS_NONE, + HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS_NONE, + reason, PM_NOP); + break; + case PM_DBS_UPGRADE: + status = policy_mgr_pdev_set_hw_mode(psoc, session_id, + HW_MODE_SS_2x2, + HW_MODE_80_MHZ, + HW_MODE_SS_2x2, HW_MODE_80_MHZ, + HW_MODE_MAC_BAND_NONE, + HW_MODE_DBS, + HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS_NONE, + reason, PM_UPGRADE); + break; + case PM_SBS_DOWNGRADE: + status = policy_mgr_complete_action(psoc, POLICY_MGR_RX_NSS_1, + PM_SBS, reason, session_id); + break; + case PM_SBS: + status = policy_mgr_pdev_set_hw_mode(psoc, session_id, + HW_MODE_SS_1x1, + HW_MODE_80_MHZ, + HW_MODE_SS_1x1, HW_MODE_80_MHZ, + HW_MODE_MAC_BAND_NONE, + HW_MODE_DBS, + HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS, + reason, PM_NOP); + break; + case PM_DOWNGRADE: + /* + * check if we have a beaconing entity that advertised 2x2 + * intially. If yes, update the beacon template & notify FW. + */ + status = policy_mgr_nss_update(psoc, POLICY_MGR_RX_NSS_1, + PM_NOP, POLICY_MGR_ANY, reason, + session_id); + break; + case PM_UPGRADE: + /* + * check if we have a beaconing entity that advertised 2x2 + * intially. If yes, update the beacon template & notify FW. + */ + status = policy_mgr_nss_update(psoc, POLICY_MGR_RX_NSS_2, + PM_NOP, POLICY_MGR_ANY, reason, + session_id); + break; + case PM_DBS1_DOWNGRADE: + status = policy_mgr_complete_action(psoc, POLICY_MGR_RX_NSS_1, + PM_DBS1, reason, + session_id); + break; + case PM_DBS2_DOWNGRADE: + status = policy_mgr_complete_action(psoc, POLICY_MGR_RX_NSS_1, + PM_DBS2, reason, + session_id); + break; + case PM_DBS1: + /* + * PM_DBS1 (2x2 5G + 1x1 2G) will support 5G 2x2. If previous + * mode is DBS, that should be 2x2 2G + 1x1 5G mode and + * the 5G band was downgraded to 1x1. So, we need to + * upgrade 5G vdevs after hw mode change. + */ + if (hw_mode.dbs_cap) + next_action = PM_UPGRADE_5G; + else + next_action = PM_NOP; + status = policy_mgr_pdev_set_hw_mode( + psoc, session_id, + HW_MODE_SS_2x2, + HW_MODE_80_MHZ, + HW_MODE_SS_1x1, HW_MODE_40_MHZ, + HW_MODE_MAC_BAND_5G, + HW_MODE_DBS, + HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS_NONE, + reason, next_action); + break; + case PM_DBS2: + /* + * PM_DBS2 (2x2 2G + 1x1 5G) will support 2G 2x2. If previous + * mode is DBS, that should be 2x2 5G + 1x1 2G mode and + * the 2G band was downgraded to 1x1. So, we need to + * upgrade 5G vdevs after hw mode change. + */ + if (hw_mode.dbs_cap) + next_action = PM_UPGRADE_2G; + else + next_action = PM_NOP; + status = policy_mgr_pdev_set_hw_mode( + psoc, session_id, + HW_MODE_SS_2x2, + HW_MODE_40_MHZ, + HW_MODE_SS_1x1, HW_MODE_40_MHZ, + HW_MODE_MAC_BAND_2G, + HW_MODE_DBS, + HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS_NONE, + reason, next_action); + break; + case PM_UPGRADE_5G: + status = policy_mgr_nss_update( + psoc, POLICY_MGR_RX_NSS_2, + PM_NOP, POLICY_MGR_BAND_5, reason, + session_id); + break; + case PM_UPGRADE_2G: + status = policy_mgr_nss_update( + psoc, POLICY_MGR_RX_NSS_2, + PM_NOP, POLICY_MGR_BAND_24, reason, + session_id); + break; + default: + policy_mgr_err("unexpected action value %d", action); + status = QDF_STATUS_E_FAILURE; + break; + } + + return status; +} + +QDF_STATUS policy_mgr_handle_conc_multiport(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, uint8_t channel) +{ + QDF_STATUS status; + + if (!policy_mgr_check_for_session_conc(psoc, session_id, channel)) { + policy_mgr_err("Conc not allowed for the session %d", + session_id); + return QDF_STATUS_E_FAILURE; + } + + status = policy_mgr_reset_connection_update(psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) + policy_mgr_err("clearing event failed"); + + status = policy_mgr_current_connections_update(psoc, session_id, + channel, + POLICY_MGR_UPDATE_REASON_NORMAL_STA); + if (QDF_STATUS_E_FAILURE == status) { + policy_mgr_err("connections update failed"); + return status; + } + + return status; +} + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +void policy_mgr_update_user_config_sap_chan( + struct wlan_objmgr_psoc *psoc, uint32_t channel) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid pm context and failed to update the user config sap channel"); + return; + } + pm_ctx->user_config_sap_channel = channel; +} + +/** + * policy_mgr_is_restart_sap_allowed() - Check if restart SAP + * allowed during SCC -> MCC switch + * @psoc: PSOC object data + * @mcc_to_scc_switch: MCC to SCC switch enabled user config + * + * Check if restart SAP allowed during SCC->MCC switch + * + * Restart: true or false + */ +static bool policy_mgr_is_restart_sap_allowed( + struct wlan_objmgr_psoc *psoc, + uint32_t mcc_to_scc_switch) +{ + uint32_t sta_ap_bit_mask = QDF_STA_MASK | QDF_SAP_MASK; + uint32_t sta_go_bit_mask = QDF_STA_MASK | QDF_P2P_GO_MASK; + + if ((mcc_to_scc_switch == QDF_MCC_TO_SCC_SWITCH_DISABLE) || + !policy_mgr_concurrent_open_sessions_running(psoc) || + !(((policy_mgr_get_concurrency_mode(psoc) & sta_ap_bit_mask) + == sta_ap_bit_mask) || + ((mcc_to_scc_switch == + QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION) && + ((policy_mgr_get_concurrency_mode(psoc) & sta_go_bit_mask) + == sta_go_bit_mask)))) { + policy_mgr_debug("MCC switch disabled or not concurrent STA/SAP, STA/GO"); + return false; + } + + return true; +} + +bool policy_mgr_is_safe_channel(struct wlan_objmgr_psoc *psoc, + uint8_t channel) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool is_safe = true; + uint8_t j; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return is_safe; + } + + + if (pm_ctx->unsafe_channel_count == 0) { + policy_mgr_debug("There are no unsafe channels"); + return is_safe; + } + + for (j = 0; j < pm_ctx->unsafe_channel_count; j++) { + if (channel == pm_ctx->unsafe_channel_list[j]) { + is_safe = false; + policy_mgr_warn("CH %d is not safe", channel); + break; + } + } + + return is_safe; +} + +bool policy_mgr_is_sap_restart_required_after_sta_disconnect( + struct wlan_objmgr_psoc *psoc, uint8_t *intf_ch) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint8_t sap_chan = policy_mgr_mode_specific_get_channel(psoc, + PM_SAP_MODE); + bool sta_sap_scc_on_dfs_chan = + policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc); + + *intf_ch = 0; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid pm context"); + return false; + } + + policy_mgr_debug("sta_sap_scc_on_dfs_chan %u, sap_chan %u", + sta_sap_scc_on_dfs_chan, sap_chan); + + if ((!sta_sap_scc_on_dfs_chan || + !(sap_chan && WLAN_REG_IS_5GHZ_CH(sap_chan) && + (wlan_reg_get_channel_state(pm_ctx->pdev, sap_chan) == + CHANNEL_STATE_DFS))) && + (!policy_mgr_sta_sap_scc_on_lte_coex_chan(psoc) || + policy_mgr_is_safe_channel(psoc, sap_chan))) { + return false; + } + + *intf_ch = pm_ctx->user_config_sap_channel; + policy_mgr_debug("Standalone SAP is not allowed on DFS channel, Move it to channel %u", + *intf_ch); + + return true; +} + +static void __policy_mgr_check_sta_ap_concurrent_ch_intf(void *data) +{ + struct wlan_objmgr_psoc *psoc; + struct policy_mgr_psoc_priv_obj *pm_ctx = NULL; + struct sta_ap_intf_check_work_ctx *work_info = NULL; + uint32_t mcc_to_scc_switch, cc_count = 0, i; + QDF_STATUS status; + uint8_t channel, sec_ch; + uint8_t operating_channel[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t vdev_id[MAX_NUMBER_OF_CONC_CONNECTIONS]; + + if (qdf_is_module_state_transitioning()) { + policy_mgr_err("Module transition in progress"); + goto end; + } + + work_info = (struct sta_ap_intf_check_work_ctx *) data; + if (!work_info) { + policy_mgr_err("Invalid work_info"); + goto end; + } + + psoc = work_info->psoc; + if (!psoc) { + policy_mgr_err("Invalid psoc"); + goto end; + } + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + goto end; + } + mcc_to_scc_switch = + policy_mgr_get_mcc_to_scc_switch_mode(psoc); + + policy_mgr_info("Concurrent open sessions running: %d", + policy_mgr_concurrent_open_sessions_running(psoc)); + + if (!policy_mgr_is_restart_sap_allowed(psoc, mcc_to_scc_switch)) + goto end; + + cc_count = policy_mgr_get_mode_specific_conn_info(psoc, + &operating_channel[cc_count], + &vdev_id[cc_count], + PM_SAP_MODE); + policy_mgr_debug("Number of concurrent SAP: %d", cc_count); + if (cc_count < MAX_NUMBER_OF_CONC_CONNECTIONS) + cc_count = cc_count + + policy_mgr_get_mode_specific_conn_info + (psoc, + &operating_channel[cc_count], + &vdev_id[cc_count], + PM_P2P_GO_MODE); + policy_mgr_debug("Number of beaconing entities (SAP + GO):%d", + cc_count); + if (!cc_count) { + policy_mgr_err("Could not retrieve SAP/GO operating channel&vdevid"); + goto end; + } + + policy_mgr_debug("wait if channel switch is already in progress"); + status = qdf_wait_single_event( + &pm_ctx->channel_switch_complete_evt, + CHANNEL_SWITCH_COMPLETE_TIMEOUT); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("wait for event failed, still continue with channel switch"); + } + if (!pm_ctx->hdd_cbacks.wlan_hdd_get_channel_for_sap_restart) { + policy_mgr_err("SAP restart get channel callback in NULL"); + goto end; + } + if (cc_count < MAX_NUMBER_OF_CONC_CONNECTIONS) + for (i = 0; i < cc_count; i++) { + status = pm_ctx->hdd_cbacks. + wlan_hdd_get_channel_for_sap_restart + (psoc, + vdev_id[i], &channel, &sec_ch); + if (status == QDF_STATUS_SUCCESS) { + policy_mgr_info("SAP restarts due to MCC->SCC switch, old chan :%d new chan: %d" + , operating_channel[i], channel); + break; + } + } + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Failed to switch SAP channel"); +end: + if (work_info) { + qdf_mem_free(work_info); + if (pm_ctx) + pm_ctx->sta_ap_intf_check_work_info = NULL; + } +} + +void policy_mgr_check_sta_ap_concurrent_ch_intf(void *data) +{ + qdf_ssr_protect(__func__); + __policy_mgr_check_sta_ap_concurrent_ch_intf(data); + qdf_ssr_unprotect(__func__); +} + +static bool policy_mgr_valid_sta_channel_check(struct wlan_objmgr_psoc *psoc, + uint8_t sta_channel) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return false; + } + + if ((wlan_reg_is_dfs_ch(pm_ctx->pdev, sta_channel) && + (!policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc))) || + wlan_reg_is_passive_or_disable_ch(pm_ctx->pdev, sta_channel) || + !policy_mgr_is_safe_channel(psoc, sta_channel)) { + if (policy_mgr_is_hw_dbs_capable(psoc)) + return true; + else + return false; + } + else + return true; +} + +QDF_STATUS policy_mgr_valid_sap_conc_channel_check( + struct wlan_objmgr_psoc *psoc, uint8_t *con_ch, uint8_t sap_ch) +{ + uint8_t channel = *con_ch; + uint8_t temp_channel = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool sta_sap_scc_on_dfs_chan; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + /* + * if force SCC is set, Check if conc channel is DFS + * or passive or part of LTE avoided channel list. + * In that case move SAP to other band if DBS is supported, + * return otherwise + */ + if (!policy_mgr_is_force_scc(psoc)) + return QDF_STATUS_SUCCESS; + + /* + * if interference is 0, check if it is DBS case. If DBS case + * return from here. If SCC, check further if SAP can move to + * STA's channel. + */ + if (!channel && + (sap_ch != policy_mgr_mode_specific_get_channel( + psoc, PM_STA_MODE))) + return QDF_STATUS_SUCCESS; + else if (!channel) + channel = sap_ch; + + sta_sap_scc_on_dfs_chan = + policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc); + + if (policy_mgr_valid_sta_channel_check(psoc, channel)) { + if (wlan_reg_is_dfs_ch(pm_ctx->pdev, channel) || + wlan_reg_is_passive_or_disable_ch(pm_ctx->pdev, channel) || + !(policy_mgr_sta_sap_scc_on_lte_coex_chan(psoc) || + policy_mgr_is_safe_channel(psoc, channel)) || + (!reg_is_etsi13_srd_chan_allowed_master_mode(pm_ctx->pdev) + && reg_is_etsi13_srd_chan(pm_ctx->pdev, channel))) { + if (wlan_reg_is_dfs_ch(pm_ctx->pdev, channel) && + sta_sap_scc_on_dfs_chan) { + policy_mgr_debug("STA SAP SCC is allowed on DFS channel"); + goto update_chan; + } + + if (policy_mgr_is_hw_dbs_capable(psoc)) { + temp_channel = + policy_mgr_get_alternate_channel_for_sap(psoc); + policy_mgr_debug("temp_channel is %d", + temp_channel); + if (temp_channel) { + channel = temp_channel; + } else { + if (WLAN_REG_IS_5GHZ_CH(channel)) + channel = PM_24_GHZ_CHANNEL_6; + else + channel = PM_5_GHZ_CHANNEL_36; + } + if (!policy_mgr_is_safe_channel( + psoc, channel)) { + policy_mgr_warn( + "Can't have concurrency on %d as it is not safe", + channel); + return QDF_STATUS_E_FAILURE; + } + } else { + policy_mgr_warn("Can't have concurrency on %d", + channel); + return QDF_STATUS_E_FAILURE; + } + } + } + +update_chan: + if (channel != sap_ch) + *con_ch = channel; + + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_check_concurrent_intf_and_restart_sap() - Check + * concurrent change intf + * @psoc: PSOC object information + * @operation_channel: operation channel + * @vdev_id: vdev id of SAP + * + * Checks the concurrent change interface and restarts SAP + * + * Return: None + */ +void policy_mgr_check_concurrent_intf_and_restart_sap( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t mcc_to_scc_switch; + uint8_t operating_channel[MAX_NUMBER_OF_CONC_CONNECTIONS] = {0}; + uint8_t vdev_id[MAX_NUMBER_OF_CONC_CONNECTIONS] = {0}; + uint32_t cc_count = 0; + bool restart_sap = false; + uint8_t sap_ch; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return; + } + if (policy_mgr_get_connection_count(psoc) == 1) { + /* + * If STA+SAP sessions are on DFS channel and STA+SAP SCC is + * enabled on DFS channel then move the SAP out of DFS channel + * as soon as STA gets disconnect. + * If STA+SAP sessions are on unsafe channel and STA+SAP SCC is + * enabled on unsafe channel then move the SAP to safe channel + * as soon as STA disconnected. + */ + if (policy_mgr_is_sap_restart_required_after_sta_disconnect( + psoc, &sap_ch)) { + policy_mgr_debug("move the SAP to configured channel %u", + sap_ch); + restart_sap = true; + goto sap_restart; + } + } + + /* + * force SCC with STA+STA+SAP will need some additional logic + */ + cc_count = policy_mgr_get_mode_specific_conn_info(psoc, + &operating_channel[cc_count], + &vdev_id[cc_count], PM_STA_MODE); + if (!cc_count) { + policy_mgr_debug("Could not get STA operating channel&vdevid"); + return; + } + + mcc_to_scc_switch = + policy_mgr_get_mcc_to_scc_switch_mode(psoc); + policy_mgr_info("MCC to SCC switch: %d chan: %d", + mcc_to_scc_switch, operating_channel[0]); + + if (!policy_mgr_is_restart_sap_allowed(psoc, mcc_to_scc_switch)) { + policy_mgr_debug( + "No action taken at check_concurrent_intf_and_restart_sap"); + return; + } + +sap_restart: + /* + * If sta_sap_scc_on_dfs_chan is true then standalone SAP is not + * allowed on DFS channel. SAP is allowed on DFS channel only when STA + * is already connected on that channel. + * In following condition restart_sap will be true if + * sta_sap_scc_on_dfs_chan is true and SAP is on DFS channel. + * This scenario can come if STA+SAP are operating on DFS channel and + * STA gets disconnected. + */ + if (restart_sap || + ((mcc_to_scc_switch != QDF_MCC_TO_SCC_SWITCH_DISABLE) && + policy_mgr_valid_sta_channel_check(psoc, operating_channel[0]) && + !pm_ctx->sta_ap_intf_check_work_info)) { + struct sta_ap_intf_check_work_ctx *work_info; + work_info = qdf_mem_malloc( + sizeof(struct sta_ap_intf_check_work_ctx)); + pm_ctx->sta_ap_intf_check_work_info = work_info; + if (work_info) { + work_info->psoc = psoc; + qdf_create_work(0, &pm_ctx->sta_ap_intf_check_work, + policy_mgr_check_sta_ap_concurrent_ch_intf, + work_info); + qdf_sched_work(0, &pm_ctx->sta_ap_intf_check_work); + policy_mgr_info( + "Checking for Concurrent Change interference"); + } + } +} +#endif /* FEATURE_WLAN_MCC_TO_SCC_SWITCH */ + +#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH +/** + * policy_mgr_change_sap_channel_with_csa() - Move SAP channel using (E)CSA + * @psoc: PSOC object information + * @vdev_id: Vdev id + * @channel: Channel to change + * @ch_width: channel width to change + * @forced: Force to switch channel, ignore SCC/MCC check + * + * Invoke the callback function to change SAP channel using (E)CSA + * + * Return: None + */ +void policy_mgr_change_sap_channel_with_csa(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, uint32_t channel, + uint32_t ch_width, + bool forced) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return; + } + + if (pm_ctx->hdd_cbacks.sap_restart_chan_switch_cb) { + policy_mgr_info("SAP change change without restart"); + pm_ctx->hdd_cbacks.sap_restart_chan_switch_cb(psoc, + vdev_id, channel, ch_width, forced); + } +} +#endif + +QDF_STATUS policy_mgr_wait_for_connection_update(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *policy_mgr_context; + + policy_mgr_context = policy_mgr_get_context(psoc); + if (!policy_mgr_context) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + status = qdf_wait_single_event( + &policy_mgr_context->connection_update_done_evt, + CONNECTION_UPDATE_TIMEOUT); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("wait for event failed"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_reset_connection_update(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *policy_mgr_context; + + policy_mgr_context = policy_mgr_get_context(psoc); + if (!policy_mgr_context) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + status = qdf_event_reset( + &policy_mgr_context->connection_update_done_evt); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("clear event failed"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_set_connection_update(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *policy_mgr_context; + + policy_mgr_context = policy_mgr_get_context(psoc); + if (!policy_mgr_context) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + status = qdf_event_set(&policy_mgr_context->connection_update_done_evt); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("set event failed"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_set_chan_switch_complete_evt( + struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *policy_mgr_context; + + policy_mgr_context = policy_mgr_get_context(psoc); + + if (!policy_mgr_context) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + status = qdf_event_set( + &policy_mgr_context->channel_switch_complete_evt); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("set event failed"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_reset_chan_switch_complete_evt( + struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *policy_mgr_context; + + policy_mgr_context = policy_mgr_get_context(psoc); + + if (!policy_mgr_context) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + status = qdf_event_reset( + &policy_mgr_context->channel_switch_complete_evt); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("reset event failed"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_set_opportunistic_update(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *policy_mgr_context; + + policy_mgr_context = policy_mgr_get_context(psoc); + if (!policy_mgr_context) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + status = qdf_event_set( + &policy_mgr_context->opportunistic_update_done_evt); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("set event failed"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_stop_opportunistic_timer(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *policy_mgr_ctx; + + policy_mgr_ctx = policy_mgr_get_context(psoc); + if (!policy_mgr_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + if (policy_mgr_ctx->dbs_opportunistic_timer.state != + QDF_TIMER_STATE_RUNNING) + return QDF_STATUS_SUCCESS; + + qdf_mc_timer_stop(&policy_mgr_ctx->dbs_opportunistic_timer); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_restart_opportunistic_timer( + struct wlan_objmgr_psoc *psoc, bool check_state) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct policy_mgr_psoc_priv_obj *policy_mgr_ctx; + + policy_mgr_ctx = policy_mgr_get_context(psoc); + if (!policy_mgr_ctx) { + policy_mgr_err("Invalid context"); + return status; + } + + if (check_state && + QDF_TIMER_STATE_RUNNING != + policy_mgr_ctx->dbs_opportunistic_timer.state) + return status; + + qdf_mc_timer_stop(&policy_mgr_ctx->dbs_opportunistic_timer); + + status = qdf_mc_timer_start( + &policy_mgr_ctx->dbs_opportunistic_timer, + DBS_OPPORTUNISTIC_TIME * 1000); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("failed to start opportunistic timer"); + return status; + } + + return status; +} + +QDF_STATUS policy_mgr_set_hw_mode_on_channel_switch( + struct wlan_objmgr_psoc *psoc, uint8_t session_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE, qdf_status; + enum policy_mgr_conc_next_action action; + + if (!policy_mgr_is_hw_dbs_capable(psoc)) { + policy_mgr_err("PM/DBS is disabled"); + return status; + } + + action = (*policy_mgr_get_current_pref_hw_mode_ptr)(psoc); + if ((action != PM_DBS_DOWNGRADE) && + (action != PM_SINGLE_MAC_UPGRADE) && + (action != PM_DBS1_DOWNGRADE) && + (action != PM_DBS2_DOWNGRADE)) { + policy_mgr_err("Invalid action: %d", action); + status = QDF_STATUS_SUCCESS; + goto done; + } + + policy_mgr_debug("action:%d session id:%d", action, session_id); + + /* Opportunistic timer is started, PM will check if MCC upgrade can be + * done on timer expiry. This avoids any possible ping pong effect + * as well. + */ + if (action == PM_SINGLE_MAC_UPGRADE) { + qdf_status = policy_mgr_restart_opportunistic_timer( + psoc, false); + if (QDF_IS_STATUS_SUCCESS(qdf_status)) + policy_mgr_debug("opportunistic timer for MCC upgrade"); + goto done; + } + + /* For DBS, we want to move right away to DBS mode */ + status = policy_mgr_next_actions(psoc, session_id, action, + POLICY_MGR_UPDATE_REASON_CHANNEL_SWITCH); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("no set hw mode command was issued"); + goto done; + } +done: + /* success must be returned only when a set hw mode was done */ + return status; +} + +void policy_mgr_checkn_update_hw_mode_single_mac_mode( + struct wlan_objmgr_psoc *psoc, uint8_t channel) +{ + uint8_t i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + if (QDF_TIMER_STATE_RUNNING == + pm_ctx->dbs_opportunistic_timer.state) + qdf_mc_timer_stop(&pm_ctx->dbs_opportunistic_timer); + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { + if (pm_conc_connection_list[i].in_use) { + if (!WLAN_REG_IS_SAME_BAND_CHANNELS(channel, + pm_conc_connection_list[i].chan)) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_debug("DBS required"); + return; + } + if (policy_mgr_is_hw_dbs_2x2_capable(psoc) && + (WLAN_REG_IS_24GHZ_CH(channel) || + WLAN_REG_IS_24GHZ_CH + (pm_conc_connection_list[i].chan))) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_debug("DBS required"); + return; + } + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + pm_dbs_opportunistic_timer_handler((void *)psoc); +} + +void policy_mgr_check_and_stop_opportunistic_timer( + struct wlan_objmgr_psoc *psoc, uint8_t id) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + enum policy_mgr_conc_next_action action = PM_NOP; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + enum policy_mgr_conn_update_reason reason; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + if (QDF_TIMER_STATE_RUNNING == + pm_ctx->dbs_opportunistic_timer.state) { + qdf_mc_timer_stop(&pm_ctx->dbs_opportunistic_timer); + action = policy_mgr_need_opportunistic_upgrade(psoc, &reason); + if (action) { + qdf_event_reset(&pm_ctx->opportunistic_update_done_evt); + status = policy_mgr_next_actions(psoc, id, action, + reason); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed in policy_mgr_next_actions"); + return; + } + status = qdf_wait_single_event( + &pm_ctx->opportunistic_update_done_evt, + CONNECTION_UPDATE_TIMEOUT); + + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("wait for event failed"); + return; + } + } + } +} + +void policy_mgr_set_hw_mode_change_in_progress( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_hw_mode_change value) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + pm_ctx->hw_mode_change_in_progress = value; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + policy_mgr_debug("hw_mode_change_in_progress:%d", value); +} + +enum policy_mgr_hw_mode_change policy_mgr_is_hw_mode_change_in_progress( + struct wlan_objmgr_psoc *psoc) +{ + enum policy_mgr_hw_mode_change value; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + value = POLICY_MGR_HW_MODE_NOT_IN_PROGRESS; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return value; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + value = pm_ctx->hw_mode_change_in_progress; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return value; +} + +enum policy_mgr_hw_mode_change policy_mgr_get_hw_mode_change_from_hw_mode_index( + struct wlan_objmgr_psoc *psoc, uint32_t hw_mode_index) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + struct policy_mgr_hw_mode_params hw_mode; + enum policy_mgr_hw_mode_change value + = POLICY_MGR_HW_MODE_NOT_IN_PROGRESS; + QDF_STATUS status; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return value; + } + + status = policy_mgr_get_hw_mode_from_idx(psoc, hw_mode_index, &hw_mode); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to get HW mode index"); + return value; + } + + if (hw_mode.dbs_cap) { + policy_mgr_info("DBS is requested with HW (%d)", + hw_mode_index); + value = POLICY_MGR_DBS_IN_PROGRESS; + goto ret_value; + } + + if (hw_mode.sbs_cap) { + policy_mgr_info("SBS is requested with HW (%d)", + hw_mode_index); + value = POLICY_MGR_SBS_IN_PROGRESS; + goto ret_value; + } + + value = POLICY_MGR_SMM_IN_PROGRESS; + policy_mgr_info("SMM is requested with HW (%d)", hw_mode_index); + +ret_value: + return value; +} + +#ifdef MPC_UT_FRAMEWORK +QDF_STATUS policy_mgr_update_connection_info_utfw( + struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, uint32_t tx_streams, uint32_t rx_streams, + uint32_t chain_mask, uint32_t type, uint32_t sub_type, + uint32_t channelid, uint32_t mac_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t conn_index = 0, found = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return status; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (vdev_id == pm_conc_connection_list[conn_index].vdev_id) { + /* debug msg */ + found = 1; + break; + } + conn_index++; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + if (!found) { + /* err msg */ + policy_mgr_err("can't find vdev_id %d in pm_conc_connection_list", + vdev_id); + return status; + } + policy_mgr_debug("--> updating entry at index[%d]", conn_index); + + policy_mgr_update_conc_list(psoc, conn_index, + policy_mgr_get_mode(type, sub_type), + channelid, HW_MODE_20_MHZ, + mac_id, chain_mask, 0, vdev_id, true, true); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_incr_connection_count_utfw(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, uint32_t tx_streams, uint32_t rx_streams, + uint32_t chain_mask, uint32_t type, uint32_t sub_type, + uint32_t channelid, uint32_t mac_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t conn_index = 0; + bool update_conn = true; + enum policy_mgr_con_mode mode; + + conn_index = policy_mgr_get_connection_count(psoc); + if (MAX_NUMBER_OF_CONC_CONNECTIONS <= conn_index) { + /* err msg */ + policy_mgr_err("exceeded max connection limit %d", + MAX_NUMBER_OF_CONC_CONNECTIONS); + return status; + } + policy_mgr_debug("--> filling entry at index[%d]", conn_index); + + mode = policy_mgr_get_mode(type, sub_type); + if (mode == PM_STA_MODE || mode == PM_P2P_CLIENT_MODE) + update_conn = false; + + policy_mgr_update_conc_list(psoc, conn_index, + mode, + channelid, HW_MODE_20_MHZ, + mac_id, chain_mask, 0, vdev_id, true, + update_conn); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_decr_connection_count_utfw(struct wlan_objmgr_psoc *psoc, + uint32_t del_all, uint32_t vdev_id) +{ + QDF_STATUS status; + + if (del_all) { + status = policy_mgr_psoc_disable(psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("Policy manager initialization failed"); + return QDF_STATUS_E_FAILURE; + } + status = policy_mgr_psoc_enable(psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("Policy manager initialization failed"); + return QDF_STATUS_E_FAILURE; + } + } else { + policy_mgr_decr_connection_count(psoc, vdev_id); + } + + return QDF_STATUS_SUCCESS; +} + +enum policy_mgr_pcl_type policy_mgr_get_pcl_from_first_conn_table( + enum policy_mgr_con_mode type, + enum policy_mgr_conc_priority_mode sys_pref) +{ + if ((sys_pref >= PM_MAX_CONC_PRIORITY_MODE) || + (type >= PM_MAX_NUM_OF_MODE)) + return PM_MAX_PCL_TYPE; + return first_connection_pcl_table[type][sys_pref]; +} + +enum policy_mgr_pcl_type policy_mgr_get_pcl_from_second_conn_table( + enum policy_mgr_one_connection_mode idx, enum policy_mgr_con_mode type, + enum policy_mgr_conc_priority_mode sys_pref, uint8_t dbs_capable) +{ + if ((idx >= PM_MAX_ONE_CONNECTION_MODE) || + (sys_pref >= PM_MAX_CONC_PRIORITY_MODE) || + (type >= PM_MAX_NUM_OF_MODE)) + return PM_MAX_PCL_TYPE; + if (dbs_capable) + return (*second_connection_pcl_dbs_table)[idx][type][sys_pref]; + else + return second_connection_pcl_nodbs_table[idx][type][sys_pref]; +} + +enum policy_mgr_pcl_type policy_mgr_get_pcl_from_third_conn_table( + enum policy_mgr_two_connection_mode idx, enum policy_mgr_con_mode type, + enum policy_mgr_conc_priority_mode sys_pref, uint8_t dbs_capable) +{ + if ((idx >= PM_MAX_TWO_CONNECTION_MODE) || + (sys_pref >= PM_MAX_CONC_PRIORITY_MODE) || + (type >= PM_MAX_NUM_OF_MODE)) + return PM_MAX_PCL_TYPE; + if (dbs_capable) + return (*third_connection_pcl_dbs_table)[idx][type][sys_pref]; + else + return third_connection_pcl_nodbs_table[idx][type][sys_pref]; +} +#endif diff --git a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_core.c b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_core.c new file mode 100644 index 000000000000..f4dc476c016c --- /dev/null +++ b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_core.c @@ -0,0 +1,3135 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_policy_mgr_core.c + * + * WLAN Concurrenct Connection Management functions + * + */ + +/* Include files */ + +#include "wlan_policy_mgr_i.h" +#include "qdf_types.h" +#include "qdf_trace.h" +#include "wlan_objmgr_global_obj.h" + +#define POLICY_MGR_MAX_CON_STRING_LEN 100 + +struct policy_mgr_conc_connection_info + pm_conc_connection_list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + +struct policy_mgr_psoc_priv_obj *policy_mgr_get_context( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + pm_ctx = (struct policy_mgr_psoc_priv_obj *) + wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_POLICY_MGR); + return pm_ctx; +} + +/** + * policy_mgr_get_updated_scan_config() - Get the updated scan configuration + * @scan_config: Pointer containing the updated scan config + * @dbs_scan: 0 or 1 indicating if DBS scan needs to be enabled/disabled + * @dbs_plus_agile_scan: 0 or 1 indicating if DBS plus agile scan needs to be + * enabled/disabled + * @single_mac_scan_with_dfs: 0 or 1 indicating if single MAC scan with DFS + * needs to be enabled/disabled + * + * Takes the current scan configuration and set the necessary scan config + * bits to either 0/1 and provides the updated value to the caller who + * can use this to pass it on to the FW + * + * Return: 0 on success + */ +QDF_STATUS policy_mgr_get_updated_scan_config( + struct wlan_objmgr_psoc *psoc, + uint32_t *scan_config, + bool dbs_scan, + bool dbs_plus_agile_scan, + bool single_mac_scan_with_dfs) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + *scan_config = pm_ctx->dual_mac_cfg.cur_scan_config; + + WMI_DBS_CONC_SCAN_CFG_DBS_SCAN_SET(*scan_config, dbs_scan); + WMI_DBS_CONC_SCAN_CFG_AGILE_SCAN_SET(*scan_config, + dbs_plus_agile_scan); + WMI_DBS_CONC_SCAN_CFG_AGILE_DFS_SCAN_SET(*scan_config, + single_mac_scan_with_dfs); + + policy_mgr_debug("scan_config:%x ", *scan_config); + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_get_updated_fw_mode_config() - Get the updated fw + * mode configuration + * @fw_mode_config: Pointer containing the updated fw mode config + * @dbs: 0 or 1 indicating if DBS needs to be enabled/disabled + * @agile_dfs: 0 or 1 indicating if agile DFS needs to be enabled/disabled + * + * Takes the current fw mode configuration and set the necessary fw mode config + * bits to either 0/1 and provides the updated value to the caller who + * can use this to pass it on to the FW + * + * Return: 0 on success + */ +QDF_STATUS policy_mgr_get_updated_fw_mode_config( + struct wlan_objmgr_psoc *psoc, + uint32_t *fw_mode_config, + bool dbs, + bool agile_dfs) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + *fw_mode_config = pm_ctx->dual_mac_cfg.cur_fw_mode_config; + + WMI_DBS_FW_MODE_CFG_DBS_SET(*fw_mode_config, dbs); + WMI_DBS_FW_MODE_CFG_AGILE_DFS_SET(*fw_mode_config, agile_dfs); + + policy_mgr_debug("fw_mode_config:%x ", *fw_mode_config); + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_is_dual_mac_disabled_in_ini() - Check if dual mac + * is disabled in INI + * + * Checks if the dual mac feature is disabled in INI + * + * Return: true if the dual mac connection is disabled from INI + */ +bool policy_mgr_is_dual_mac_disabled_in_ini( + struct wlan_objmgr_psoc *psoc) +{ + bool is_disabled = false; + enum dbs_support dbs_type = wlan_objmgr_psoc_get_dual_mac_disable(psoc); + + /* + * If DBS support for connection is disabled through INI then assume + * that DBS is not supported, so that policy manager takes + * the decision considering non-dbs cases only. + * + * For DBS scan check the INI value explicitly + */ + switch (dbs_type) { + case DISABLE_DBS_CXN_AND_SCAN: + case DISABLE_DBS_CXN_AND_ENABLE_DBS_SCAN: + case DISABLE_DBS_CXN_AND_ENABLE_DBS_SCAN_WITH_ASYNC_SCAN_OFF: + is_disabled = true; + break; + default: + break; + } + + return is_disabled; +} + +uint32_t policy_mgr_get_mcc_to_scc_switch_mode( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return 0; + } + + return pm_ctx->cfg.mcc_to_scc_switch; +} + +/** + * policy_mgr_get_dbs_config() - Get DBS bit + * + * Gets the DBS bit of fw_mode_config_bits + * + * Return: 0 or 1 to indicate the DBS bit + */ +bool policy_mgr_get_dbs_config(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t fw_mode_config; + + if (policy_mgr_is_dual_mac_disabled_in_ini(psoc)) + return false; + + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + /* We take that it is disabled and proceed */ + return false; + } + fw_mode_config = pm_ctx->dual_mac_cfg.cur_fw_mode_config; + + return WMI_DBS_FW_MODE_CFG_DBS_GET(fw_mode_config); +} + +/** + * policy_mgr_get_agile_dfs_config() - Get Agile DFS bit + * + * Gets the Agile DFS bit of fw_mode_config_bits + * + * Return: 0 or 1 to indicate the Agile DFS bit + */ +bool policy_mgr_get_agile_dfs_config(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t fw_mode_config; + + if (policy_mgr_is_dual_mac_disabled_in_ini(psoc)) + return false; + + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + /* We take that it is disabled and proceed */ + return false; + } + fw_mode_config = pm_ctx->dual_mac_cfg.cur_fw_mode_config; + + return WMI_DBS_FW_MODE_CFG_AGILE_DFS_GET(fw_mode_config); +} + +/** + * policy_mgr_get_dbs_scan_config() - Get DBS scan bit + * + * Gets the DBS scan bit of concurrent_scan_config_bits + * + * Return: 0 or 1 to indicate the DBS scan bit + */ +bool policy_mgr_get_dbs_scan_config(struct wlan_objmgr_psoc *psoc) +{ + uint32_t scan_config; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + if (policy_mgr_is_dual_mac_disabled_in_ini(psoc)) + return false; + + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + /* We take that it is disabled and proceed */ + return false; + } + scan_config = pm_ctx->dual_mac_cfg.cur_scan_config; + + return WMI_DBS_CONC_SCAN_CFG_DBS_SCAN_GET(scan_config); +} + +/** + * policy_mgr_get_tx_rx_ss_from_config() - Get Tx/Rx spatial + * stream from HW mode config + * @mac_ss: Config which indicates the HW mode as per 'hw_mode_ss_config' + * @tx_ss: Contains the Tx spatial stream + * @rx_ss: Contains the Rx spatial stream + * + * Returns the number of spatial streams of Tx and Rx + * + * Return: None + */ +void policy_mgr_get_tx_rx_ss_from_config(enum hw_mode_ss_config mac_ss, + uint32_t *tx_ss, uint32_t *rx_ss) +{ + switch (mac_ss) { + case HW_MODE_SS_0x0: + *tx_ss = 0; + *rx_ss = 0; + break; + case HW_MODE_SS_1x1: + *tx_ss = 1; + *rx_ss = 1; + break; + case HW_MODE_SS_2x2: + *tx_ss = 2; + *rx_ss = 2; + break; + case HW_MODE_SS_3x3: + *tx_ss = 3; + *rx_ss = 3; + break; + case HW_MODE_SS_4x4: + *tx_ss = 4; + *rx_ss = 4; + break; + default: + *tx_ss = 0; + *rx_ss = 0; + } +} + +/** + * policy_mgr_get_matching_hw_mode_index() - Get matching HW mode index + * @psoc: psoc handle + * @mac0_tx_ss: Number of tx spatial streams of MAC0 + * @mac0_rx_ss: Number of rx spatial streams of MAC0 + * @mac0_bw: Bandwidth of MAC0 of type 'hw_mode_bandwidth' + * @mac1_tx_ss: Number of tx spatial streams of MAC1 + * @mac1_rx_ss: Number of rx spatial streams of MAC1 + * @mac1_bw: Bandwidth of MAC1 of type 'hw_mode_bandwidth' + * @mac0_band_cap: mac0 band capability requirement + * (0: Don't care, 1: 2.4G, 2: 5G) + * @dbs: DBS capability of type 'hw_mode_dbs_capab' + * @dfs: Agile DFS capability of type 'hw_mode_agile_dfs_capab' + * @sbs: SBS capability of type 'hw_mode_sbs_capab' + * + * Fetches the HW mode index corresponding to the HW mode provided. + * In Genoa two DBS HW modes (2x2 5G + 1x1 2G, 2x2 2G + 1x1 5G), + * the "ss" number and "bw" value are not enough to specify the expected + * HW mode. But in both HW mode, the mac0 can support either 5G or 2G. + * So, the Parameter "mac0_band_cap" will specify the expected band support + * requirement on mac 0 to find the expected HW mode. + * + * Return: Positive hw mode index in case a match is found or a negative + * value, otherwise + */ +int8_t policy_mgr_get_matching_hw_mode_index( + struct wlan_objmgr_psoc *psoc, + uint32_t mac0_tx_ss, uint32_t mac0_rx_ss, + enum hw_mode_bandwidth mac0_bw, + uint32_t mac1_tx_ss, uint32_t mac1_rx_ss, + enum hw_mode_bandwidth mac1_bw, + enum hw_mode_mac_band_cap mac0_band_cap, + enum hw_mode_dbs_capab dbs, + enum hw_mode_agile_dfs_capab dfs, + enum hw_mode_sbs_capab sbs) +{ + uint32_t i; + uint32_t t_mac0_tx_ss, t_mac0_rx_ss, t_mac0_bw; + uint32_t t_mac1_tx_ss, t_mac1_rx_ss, t_mac1_bw; + uint32_t dbs_mode, agile_dfs_mode, sbs_mode; + uint32_t t_mac0_band_cap; + int8_t found = -EINVAL; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return found; + } + + for (i = 0; i < pm_ctx->num_dbs_hw_modes; i++) { + t_mac0_tx_ss = POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + if (t_mac0_tx_ss < mac0_tx_ss) + continue; + + t_mac0_rx_ss = POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + if (t_mac0_rx_ss < mac0_rx_ss) + continue; + + t_mac0_bw = POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + /* + * Firmware advertises max bw capability as CBW 80+80 + * for single MAC. Thus CBW 20/40/80 should also be + * supported, if CBW 80+80 is supported. + */ + if (t_mac0_bw < mac0_bw) + continue; + + t_mac1_tx_ss = POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + if (t_mac1_tx_ss < mac1_tx_ss) + continue; + + t_mac1_rx_ss = POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + if (t_mac1_rx_ss < mac1_rx_ss) + continue; + + t_mac1_bw = POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + if (t_mac1_bw < mac1_bw) + continue; + + dbs_mode = POLICY_MGR_HW_MODE_DBS_MODE_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + if (dbs_mode != dbs) + continue; + + agile_dfs_mode = POLICY_MGR_HW_MODE_AGILE_DFS_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + if (agile_dfs_mode != dfs) + continue; + + sbs_mode = POLICY_MGR_HW_MODE_SBS_MODE_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + if (sbs_mode != sbs) + continue; + + t_mac0_band_cap = POLICY_MGR_HW_MODE_MAC0_BAND_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + if (mac0_band_cap && t_mac0_band_cap != mac0_band_cap) + continue; + + found = POLICY_MGR_HW_MODE_ID_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + + policy_mgr_debug("hw_mode id %d found at %d", found, i); + + break; + } + return found; +} + +/** + * policy_mgr_get_hw_mode_from_dbs_hw_list() - Get hw_mode index + * @mac0_ss: MAC0 spatial stream configuration + * @mac0_bw: MAC0 bandwidth configuration + * @mac1_ss: MAC1 spatial stream configuration + * @mac1_bw: MAC1 bandwidth configuration + * @mac0_band_cap: mac0 band capability requirement + * (0: Don't care, 1: 2.4G, 2: 5G) + * @dbs: HW DBS capability + * @dfs: HW Agile DFS capability + * @sbs: HW SBS capability + * + * Get the HW mode index corresponding to the HW modes spatial stream, + * bandwidth, DBS, Agile DFS and SBS capability + * + * In Genoa two DBS HW modes (2x2 5G + 1x1 2G, 2x2 2G + 1x1 5G), + * the "ss" number and "bw" value are not enough to specify the expected + * HW mode. But in both HW mode, the mac0 can support either 5G or 2G. + * So, the Parameter "mac0_band_cap" will specify the expected band support + * requirement on mac 0 to find the expected HW mode. + * + * Return: Index number if a match is found or -negative value if not found + */ +int8_t policy_mgr_get_hw_mode_idx_from_dbs_hw_list( + struct wlan_objmgr_psoc *psoc, + enum hw_mode_ss_config mac0_ss, + enum hw_mode_bandwidth mac0_bw, + enum hw_mode_ss_config mac1_ss, + enum hw_mode_bandwidth mac1_bw, + enum hw_mode_mac_band_cap mac0_band_cap, + enum hw_mode_dbs_capab dbs, + enum hw_mode_agile_dfs_capab dfs, + enum hw_mode_sbs_capab sbs) +{ + uint32_t mac0_tx_ss, mac0_rx_ss; + uint32_t mac1_tx_ss, mac1_rx_ss; + + policy_mgr_get_tx_rx_ss_from_config(mac0_ss, &mac0_tx_ss, &mac0_rx_ss); + policy_mgr_get_tx_rx_ss_from_config(mac1_ss, &mac1_tx_ss, &mac1_rx_ss); + + policy_mgr_debug("MAC0: TxSS=%d, RxSS=%d, BW=%d band=%d", + mac0_tx_ss, mac0_rx_ss, mac0_bw, mac0_band_cap); + policy_mgr_debug("MAC1: TxSS=%d, RxSS=%d, BW=%d", + mac1_tx_ss, mac1_rx_ss, mac1_bw); + policy_mgr_debug("DBS=%d, Agile DFS=%d, SBS=%d", + dbs, dfs, sbs); + + return policy_mgr_get_matching_hw_mode_index(psoc, mac0_tx_ss, + mac0_rx_ss, + mac0_bw, + mac1_tx_ss, mac1_rx_ss, + mac1_bw, + mac0_band_cap, + dbs, dfs, sbs); +} + +/** + * policy_mgr_get_hw_mode_from_idx() - Get HW mode based on index + * @psoc: psoc object + * @idx: HW mode id + * @hw_mode: HW mode params + * + * Fetches the HW mode parameters + * + * Return: Success if hw mode is obtained and the hw mode params + */ +QDF_STATUS policy_mgr_get_hw_mode_from_idx( + struct wlan_objmgr_psoc *psoc, + uint32_t idx, + struct policy_mgr_hw_mode_params *hw_mode) +{ + uint32_t param; + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint8_t mac0_min_ss; + uint8_t mac1_min_ss; + uint32_t i, hw_mode_id; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + if (!pm_ctx->num_dbs_hw_modes) { + policy_mgr_err("No dbs hw modes available"); + return QDF_STATUS_E_FAILURE; + } + for (i = 0; i < pm_ctx->num_dbs_hw_modes; i++) { + param = pm_ctx->hw_mode.hw_mode_list[i]; + hw_mode_id = POLICY_MGR_HW_MODE_ID_GET(param); + if (hw_mode_id == idx) + break; + } + if (i >= pm_ctx->num_dbs_hw_modes) { + policy_mgr_err("hw mode id %d not found", idx); + return QDF_STATUS_E_FAILURE; + } + + param = pm_ctx->hw_mode.hw_mode_list[i]; + + hw_mode->mac0_tx_ss = POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_GET(param); + hw_mode->mac0_rx_ss = POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_GET(param); + hw_mode->mac0_bw = POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_GET(param); + hw_mode->mac1_tx_ss = POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_GET(param); + hw_mode->mac1_rx_ss = POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_GET(param); + hw_mode->mac1_bw = POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_GET(param); + hw_mode->mac0_band_cap = POLICY_MGR_HW_MODE_MAC0_BAND_GET(param); + hw_mode->dbs_cap = POLICY_MGR_HW_MODE_DBS_MODE_GET(param); + hw_mode->agile_dfs_cap = POLICY_MGR_HW_MODE_AGILE_DFS_GET(param); + hw_mode->sbs_cap = POLICY_MGR_HW_MODE_SBS_MODE_GET(param); + if (hw_mode->dbs_cap) { + mac0_min_ss = QDF_MIN(hw_mode->mac0_tx_ss, hw_mode->mac0_rx_ss); + mac1_min_ss = QDF_MIN(hw_mode->mac1_tx_ss, hw_mode->mac1_rx_ss); + if (hw_mode->mac0_band_cap == WLAN_5G_CAPABILITY && + mac0_min_ss && mac1_min_ss && + mac0_min_ss > mac1_min_ss) + hw_mode->action_type = PM_DBS1; + else if (hw_mode->mac0_band_cap == WLAN_2G_CAPABILITY && + mac0_min_ss && mac1_min_ss && + mac0_min_ss > mac1_min_ss) + hw_mode->action_type = PM_DBS2; + else + hw_mode->action_type = PM_DBS; + } + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_get_old_and_new_hw_index() - Get the old and new HW index + * @old_hw_mode_index: Value at this pointer contains the old HW mode index + * Default value when not configured is POLICY_MGR_DEFAULT_HW_MODE_INDEX + * @new_hw_mode_index: Value at this pointer contains the new HW mode index + * Default value when not configured is POLICY_MGR_DEFAULT_HW_MODE_INDEX + * + * Get the old and new HW index configured in the driver + * + * Return: Failure in case the HW mode indices cannot be fetched and Success + * otherwise. When no HW mode transition has happened the values of + * old_hw_mode_index and new_hw_mode_index will be the same. + */ +QDF_STATUS policy_mgr_get_old_and_new_hw_index( + struct wlan_objmgr_psoc *psoc, + uint32_t *old_hw_mode_index, + uint32_t *new_hw_mode_index) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_INVAL; + } + + *old_hw_mode_index = pm_ctx->old_hw_mode_index; + *new_hw_mode_index = pm_ctx->new_hw_mode_index; + + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_update_conc_list() - Update the concurrent connection list + * @conn_index: Connection index + * @mode: Mode + * @chan: Channel + * @bw: Bandwidth + * @mac: Mac id + * @chain_mask: Chain mask + * @vdev_id: vdev id + * @in_use: Flag to indicate if the index is in use or not + * @update_conn: Flag to indicate if mode change event should + * be sent or not + * + * Updates the index value of the concurrent connection list + * + * Return: None + */ +void policy_mgr_update_conc_list(struct wlan_objmgr_psoc *psoc, + uint32_t conn_index, + enum policy_mgr_con_mode mode, + uint8_t chan, + enum hw_mode_bandwidth bw, + uint8_t mac, + enum policy_mgr_chain_mode chain_mask, + uint32_t original_nss, + uint32_t vdev_id, + bool in_use, + bool update_conn) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool mcc_mode; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + if (conn_index >= MAX_NUMBER_OF_CONC_CONNECTIONS) { + policy_mgr_err("Number of connections exceeded conn_index: %d", + conn_index); + return; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + pm_conc_connection_list[conn_index].mode = mode; + pm_conc_connection_list[conn_index].chan = chan; + pm_conc_connection_list[conn_index].bw = bw; + pm_conc_connection_list[conn_index].mac = mac; + pm_conc_connection_list[conn_index].chain_mask = chain_mask; + pm_conc_connection_list[conn_index].original_nss = original_nss; + pm_conc_connection_list[conn_index].vdev_id = vdev_id; + pm_conc_connection_list[conn_index].in_use = in_use; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + /* + * For STA and P2P client mode, the mode change event sent as part + * of the callback causes delay in processing M1 frame at supplicant + * resulting in cert test case failure. The mode change event is sent + * as part of add key for STA and P2P client mode. + */ + if (pm_ctx->mode_change_cb && update_conn) + pm_ctx->mode_change_cb(); + + policy_mgr_dump_connection_status_info(psoc); + if (pm_ctx->cdp_cbacks.cdp_update_mac_id) + pm_ctx->cdp_cbacks.cdp_update_mac_id(psoc, vdev_id, mac); + + /* IPA only cares about STA or SAP mode */ + if (mode == PM_STA_MODE || mode == PM_SAP_MODE) { + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + mcc_mode = policy_mgr_current_concurrency_is_mcc(psoc); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + if (pm_ctx->dp_cbacks.hdd_ipa_set_mcc_mode_cb) + pm_ctx->dp_cbacks.hdd_ipa_set_mcc_mode_cb(mcc_mode); + } +} + +/** + * policy_mgr_store_and_del_conn_info() - Store and del a connection info + * @mode: Mode whose entry has to be deleted + * @all_matching_cxn_to_del: All the specified mode entries should be deleted + * @info: Struture array pointer where the connection info will be saved + * @num_cxn_del: Number of connection which are going to be deleted + * + * Saves the connection info corresponding to the provided mode + * and deleted that corresponding entry based on vdev from the + * connection info structure + * + * Return: None + */ +void policy_mgr_store_and_del_conn_info(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, bool all_matching_cxn_to_del, + struct policy_mgr_conc_connection_info *info, uint8_t *num_cxn_del) +{ + int32_t conn_index = 0; + uint32_t found_index = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + if (!num_cxn_del) { + policy_mgr_err("num_cxn_del is NULL"); + return; + } + *num_cxn_del = 0; + if (!info) { + policy_mgr_err("Invalid connection info"); + return; + } + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (mode == pm_conc_connection_list[conn_index].mode) { + /* + * Storing the connection entry which will be + * temporarily deleted. + */ + info[found_index] = pm_conc_connection_list[conn_index]; + /* Deleting the connection entry */ + policy_mgr_decr_connection_count(psoc, + info[found_index].vdev_id); + policy_mgr_debug("Stored %d (%d), deleted STA entry with vdev id %d, index %d", + info[found_index].vdev_id, + info[found_index].mode, + info[found_index].vdev_id, conn_index); + found_index++; + if (all_matching_cxn_to_del) + continue; + else + break; + } + conn_index++; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + if (!found_index) { + *num_cxn_del = 0; + policy_mgr_err("Mode:%d not available in the conn info", mode); + } else { + *num_cxn_del = found_index; + policy_mgr_err("Mode:%d number of conn %d temp del", + mode, *num_cxn_del); + } + + /* + * Caller should set the PCL and restore the connection entry + * in conn info. + */ +} + +void policy_mgr_store_and_del_conn_info_by_vdev_id( + struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + struct policy_mgr_conc_connection_info *info, + uint8_t *num_cxn_del) +{ + uint32_t conn_index = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + if (!info || !num_cxn_del) { + policy_mgr_err("Invalid parameters"); + return; + } + *num_cxn_del = 0; + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if ((pm_conc_connection_list[conn_index].vdev_id == vdev_id) && + pm_conc_connection_list[conn_index].in_use) { + *num_cxn_del = 1; + break; + } + } + /* + * Storing the connection entry which will be + * temporarily deleted. + */ + if (*num_cxn_del == 1) { + *info = pm_conc_connection_list[conn_index]; + /* Deleting the connection entry */ + policy_mgr_decr_connection_count( + psoc, + pm_conc_connection_list[conn_index].vdev_id); + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); +} + +/** + * policy_mgr_restore_deleted_conn_info() - Restore connection info + * @info: An array saving connection info that is to be restored + * @num_cxn_del: Number of connection temporary deleted + * + * Restores the connection info of STA that was saved before + * updating the PCL to the FW + * + * Return: None + */ +void policy_mgr_restore_deleted_conn_info(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_conc_connection_info *info, + uint8_t num_cxn_del) +{ + uint32_t conn_index; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + if (MAX_NUMBER_OF_CONC_CONNECTIONS <= num_cxn_del || 0 == num_cxn_del) { + policy_mgr_err("Failed to restore %d/%d deleted information", + num_cxn_del, MAX_NUMBER_OF_CONC_CONNECTIONS); + return; + } + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + conn_index = policy_mgr_get_connection_count(psoc); + if (MAX_NUMBER_OF_CONC_CONNECTIONS <= conn_index) { + policy_mgr_err("Failed to restore the deleted information %d/%d", + conn_index, MAX_NUMBER_OF_CONC_CONNECTIONS); + return; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + qdf_mem_copy(&pm_conc_connection_list[conn_index], info, + num_cxn_del * sizeof(*info)); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + policy_mgr_debug("Restored the deleleted conn info, vdev:%d, index:%d", + info->vdev_id, conn_index); +} + +/** + * policy_mgr_update_hw_mode_conn_info() - Update connection + * info based on HW mode + * @num_vdev_mac_entries: Number of vdev-mac id entries that follow + * @vdev_mac_map: Mapping of vdev-mac id + * @hw_mode: HW mode + * + * Updates the connection info parameters based on the new HW mode + * + * Return: None + */ +void policy_mgr_update_hw_mode_conn_info(struct wlan_objmgr_psoc *psoc, + uint32_t num_vdev_mac_entries, + struct policy_mgr_vdev_mac_map *vdev_mac_map, + struct policy_mgr_hw_mode_params hw_mode) +{ + uint32_t i, conn_index, found; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (i = 0; i < num_vdev_mac_entries; i++) { + conn_index = 0; + found = 0; + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (vdev_mac_map[i].vdev_id == + pm_conc_connection_list[conn_index].vdev_id) { + found = 1; + break; + } + conn_index++; + } + if (found) { + pm_conc_connection_list[conn_index].mac = + vdev_mac_map[i].mac_id; + policy_mgr_debug("vdev:%d, mac:%d", + pm_conc_connection_list[conn_index].vdev_id, + pm_conc_connection_list[conn_index].mac); + if (pm_ctx->cdp_cbacks.cdp_update_mac_id) + pm_ctx->cdp_cbacks.cdp_update_mac_id( + psoc, + vdev_mac_map[i].vdev_id, + vdev_mac_map[i].mac_id); + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_dump_connection_status_info(psoc); +} + +void policy_mgr_pdev_set_hw_mode_cb(uint32_t status, + uint32_t cfgd_hw_mode_index, + uint32_t num_vdev_mac_entries, + struct policy_mgr_vdev_mac_map *vdev_mac_map, + uint8_t next_action, + enum policy_mgr_conn_update_reason reason, + uint32_t session_id, void *context) +{ + QDF_STATUS ret; + struct policy_mgr_hw_mode_params hw_mode; + uint32_t i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(context); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + policy_mgr_set_hw_mode_change_in_progress(context, + POLICY_MGR_HW_MODE_NOT_IN_PROGRESS); + + if (status != SET_HW_MODE_STATUS_OK) { + policy_mgr_err("Set HW mode failed with status %d", status); + return; + } + + if (!vdev_mac_map) { + policy_mgr_err("vdev_mac_map is NULL"); + return; + } + + policy_mgr_debug("cfgd_hw_mode_index=%d", cfgd_hw_mode_index); + + for (i = 0; i < num_vdev_mac_entries; i++) + policy_mgr_debug("vdev_id:%d mac_id:%d", + vdev_mac_map[i].vdev_id, + vdev_mac_map[i].mac_id); + + ret = policy_mgr_get_hw_mode_from_idx(context, cfgd_hw_mode_index, + &hw_mode); + if (ret != QDF_STATUS_SUCCESS) { + policy_mgr_err("Get HW mode failed: %d", ret); + return; + } + + policy_mgr_debug("MAC0: TxSS:%d, RxSS:%d, Bw:%d, band_cap %d", + hw_mode.mac0_tx_ss, hw_mode.mac0_rx_ss, + hw_mode.mac0_bw, hw_mode.mac0_band_cap); + policy_mgr_debug("MAC1: TxSS:%d, RxSS:%d, Bw:%d", + hw_mode.mac1_tx_ss, hw_mode.mac1_rx_ss, + hw_mode.mac1_bw); + policy_mgr_debug("DBS:%d, Agile DFS:%d, SBS:%d", + hw_mode.dbs_cap, hw_mode.agile_dfs_cap, + hw_mode.sbs_cap); + + /* update pm_conc_connection_list */ + policy_mgr_update_hw_mode_conn_info(context, num_vdev_mac_entries, + vdev_mac_map, + hw_mode); + if (pm_ctx->mode_change_cb) + pm_ctx->mode_change_cb(); + + ret = policy_mgr_set_connection_update(context); + if (!QDF_IS_STATUS_SUCCESS(ret)) + policy_mgr_err("ERROR: set connection_update_done event failed"); + + if (PM_NOP != next_action) + policy_mgr_next_actions(context, session_id, + next_action, reason); + else { + policy_mgr_debug("No action needed right now"); + ret = policy_mgr_set_opportunistic_update(context); + if (!QDF_IS_STATUS_SUCCESS(ret)) + policy_mgr_err("ERROR: set opportunistic_update event failed"); + } + + return; +} + +/** + * policy_mgr_dump_current_concurrency_one_connection() - To dump the + * current concurrency info with one connection + * @cc_mode: connection string + * @length: Maximum size of the string + * + * This routine is called to dump the concurrency info + * + * Return: length of the string + */ +static uint32_t policy_mgr_dump_current_concurrency_one_connection( + char *cc_mode, uint32_t length) +{ + uint32_t count = 0; + enum policy_mgr_con_mode mode; + + mode = pm_conc_connection_list[0].mode; + + switch (mode) { + case PM_STA_MODE: + count = strlcat(cc_mode, "STA", + length); + break; + case PM_SAP_MODE: + count = strlcat(cc_mode, "SAP", + length); + break; + case PM_P2P_CLIENT_MODE: + count = strlcat(cc_mode, "P2P CLI", + length); + break; + case PM_P2P_GO_MODE: + count = strlcat(cc_mode, "P2P GO", + length); + break; + case PM_IBSS_MODE: + count = strlcat(cc_mode, "IBSS", + length); + break; + default: + policy_mgr_err("unexpected mode %d", mode); + break; + } + + return count; +} + +/** + * policy_mgr_dump_current_concurrency_two_connection() - To dump the + * current concurrency info with two connections + * @cc_mode: connection string + * @length: Maximum size of the string + * + * This routine is called to dump the concurrency info + * + * Return: length of the string + */ +static uint32_t policy_mgr_dump_current_concurrency_two_connection( + char *cc_mode, uint32_t length) +{ + uint32_t count = 0; + enum policy_mgr_con_mode mode; + + mode = pm_conc_connection_list[1].mode; + + switch (mode) { + case PM_STA_MODE: + count = policy_mgr_dump_current_concurrency_one_connection( + cc_mode, length); + count += strlcat(cc_mode, "+STA", + length); + break; + case PM_SAP_MODE: + count = policy_mgr_dump_current_concurrency_one_connection( + cc_mode, length); + count += strlcat(cc_mode, "+SAP", + length); + break; + case PM_P2P_CLIENT_MODE: + count = policy_mgr_dump_current_concurrency_one_connection( + cc_mode, length); + count += strlcat(cc_mode, "+P2P CLI", + length); + break; + case PM_P2P_GO_MODE: + count = policy_mgr_dump_current_concurrency_one_connection( + cc_mode, length); + count += strlcat(cc_mode, "+P2P GO", + length); + break; + case PM_IBSS_MODE: + count = policy_mgr_dump_current_concurrency_one_connection( + cc_mode, length); + count += strlcat(cc_mode, "+IBSS", + length); + break; + default: + policy_mgr_err("unexpected mode %d", mode); + break; + } + + return count; +} + +/** + * policy_mgr_dump_current_concurrency_three_connection() - To dump the + * current concurrency info with three connections + * @cc_mode: connection string + * @length: Maximum size of the string + * + * This routine is called to dump the concurrency info + * + * Return: length of the string + */ +static uint32_t policy_mgr_dump_current_concurrency_three_connection( + char *cc_mode, uint32_t length) +{ + uint32_t count = 0; + enum policy_mgr_con_mode mode; + + mode = pm_conc_connection_list[2].mode; + + switch (mode) { + case PM_STA_MODE: + count = policy_mgr_dump_current_concurrency_two_connection( + cc_mode, length); + count += strlcat(cc_mode, "+STA", + length); + break; + case PM_SAP_MODE: + count = policy_mgr_dump_current_concurrency_two_connection( + cc_mode, length); + count += strlcat(cc_mode, "+SAP", + length); + break; + case PM_P2P_CLIENT_MODE: + count = policy_mgr_dump_current_concurrency_two_connection( + cc_mode, length); + count += strlcat(cc_mode, "+P2P CLI", + length); + break; + case PM_P2P_GO_MODE: + count = policy_mgr_dump_current_concurrency_two_connection( + cc_mode, length); + count += strlcat(cc_mode, "+P2P GO", + length); + break; + case PM_IBSS_MODE: + count = policy_mgr_dump_current_concurrency_two_connection( + cc_mode, length); + count += strlcat(cc_mode, "+IBSS", + length); + break; + default: + policy_mgr_err("unexpected mode %d", mode); + break; + } + + return count; +} + +/** + * policy_mgr_dump_dbs_concurrency() - To dump the dbs concurrency + * combination + * @cc_mode: connection string + * + * This routine is called to dump the concurrency info + * + * Return: None + */ +static void policy_mgr_dump_dbs_concurrency(struct wlan_objmgr_psoc *psoc, + char *cc_mode, uint32_t length) +{ + char buf[4] = {0}; + uint8_t mac = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + strlcat(cc_mode, " DBS", length); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if (pm_conc_connection_list[0].chan == + pm_conc_connection_list[1].chan) + strlcat(cc_mode, + " with SCC for 1st two connections on mac ", + length); + else + strlcat(cc_mode, + " with MCC for 1st two connections on mac ", + length); + mac = pm_conc_connection_list[0].mac; + } + if (pm_conc_connection_list[0].mac == pm_conc_connection_list[2].mac) { + if (pm_conc_connection_list[0].chan == + pm_conc_connection_list[2].chan) + strlcat(cc_mode, + " with SCC for 1st & 3rd connections on mac ", + length); + else + strlcat(cc_mode, + " with MCC for 1st & 3rd connections on mac ", + length); + mac = pm_conc_connection_list[0].mac; + } + if (pm_conc_connection_list[1].mac == pm_conc_connection_list[2].mac) { + if (pm_conc_connection_list[1].chan == + pm_conc_connection_list[2].chan) + strlcat(cc_mode, + " with SCC for 2nd & 3rd connections on mac ", + length); + else + strlcat(cc_mode, + " with MCC for 2nd & 3rd connections on mac ", + length); + mac = pm_conc_connection_list[1].mac; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + snprintf(buf, sizeof(buf), "%d ", mac); + strlcat(cc_mode, buf, length); +} + +/** + * policy_mgr_dump_current_concurrency() - To dump the current + * concurrency combination + * + * This routine is called to dump the concurrency info + * + * Return: None + */ +void policy_mgr_dump_current_concurrency(struct wlan_objmgr_psoc *psoc) +{ + uint32_t num_connections = 0; + char cc_mode[POLICY_MGR_MAX_CON_STRING_LEN] = {0}; + uint32_t count = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + num_connections = policy_mgr_get_connection_count(psoc); + + switch (num_connections) { + case 1: + policy_mgr_dump_current_concurrency_one_connection(cc_mode, + sizeof(cc_mode)); + policy_mgr_err("%s Standalone", cc_mode); + break; + case 2: + count = policy_mgr_dump_current_concurrency_two_connection( + cc_mode, sizeof(cc_mode)); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (pm_conc_connection_list[0].chan == + pm_conc_connection_list[1].chan) { + strlcat(cc_mode, " SCC", sizeof(cc_mode)); + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + strlcat(cc_mode, " MCC", sizeof(cc_mode)); + } else + strlcat(cc_mode, " DBS", sizeof(cc_mode)); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_err("%s", cc_mode); + break; + case 3: + count = policy_mgr_dump_current_concurrency_three_connection( + cc_mode, sizeof(cc_mode)); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if ((pm_conc_connection_list[0].chan == + pm_conc_connection_list[1].chan) && + (pm_conc_connection_list[0].chan == + pm_conc_connection_list[2].chan)){ + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + strlcat(cc_mode, " SCC", + sizeof(cc_mode)); + } else if ((pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) + && (pm_conc_connection_list[0].mac == + pm_conc_connection_list[2].mac)) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + strlcat(cc_mode, " MCC on single MAC", + sizeof(cc_mode)); + } else { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_dump_dbs_concurrency(psoc, cc_mode, + sizeof(cc_mode)); + } + policy_mgr_err("%s", cc_mode); + break; + default: + policy_mgr_err("unexpected num_connections value %d", + num_connections); + break; + } + + return; +} + +/** + * policy_mgr_pdev_set_pcl() - Sets PCL to FW + * @mode: adapter mode + * + * Fetches the PCL and sends the PCL to SME + * module which in turn will send the WMI + * command WMI_PDEV_SET_PCL_CMDID to the fw + * + * Return: None + */ +void policy_mgr_pdev_set_pcl(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode) +{ + QDF_STATUS status; + enum policy_mgr_con_mode con_mode; + struct policy_mgr_pcl_list pcl; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + pcl.pcl_len = 0; + + switch (mode) { + case QDF_STA_MODE: + con_mode = PM_STA_MODE; + break; + case QDF_P2P_CLIENT_MODE: + con_mode = PM_P2P_CLIENT_MODE; + break; + case QDF_P2P_GO_MODE: + con_mode = PM_P2P_GO_MODE; + break; + case QDF_SAP_MODE: + con_mode = PM_SAP_MODE; + break; + case QDF_IBSS_MODE: + con_mode = PM_IBSS_MODE; + break; + default: + policy_mgr_err("Unable to set PCL to FW: %d", mode); + return; + } + + policy_mgr_debug("get pcl to set it to the FW"); + + status = policy_mgr_get_pcl(psoc, con_mode, + pcl.pcl_list, &pcl.pcl_len, + pcl.weight_list, QDF_ARRAY_SIZE(pcl.weight_list)); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Unable to set PCL to FW, Get PCL failed"); + return; + } + + status = pm_ctx->sme_cbacks.sme_pdev_set_pcl(&pcl); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Send soc set PCL to SME failed"); + else + policy_mgr_debug("Set PCL to FW for mode:%d", mode); +} + +/** + * policy_mgr_set_pcl_for_existing_combo() - Set PCL for existing connection + * @mode: Connection mode of type 'policy_mgr_con_mode' + * + * Set the PCL for an existing connection + * + * Return: None + */ +void policy_mgr_set_pcl_for_existing_combo( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode) +{ + struct policy_mgr_conc_connection_info + info[MAX_NUMBER_OF_CONC_CONNECTIONS] = { {0} }; + enum QDF_OPMODE pcl_mode; + uint8_t num_cxn_del = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + pcl_mode = policy_mgr_get_qdf_mode_from_pm(mode); + if (pcl_mode == QDF_MAX_NO_OF_MODE) + return; + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (policy_mgr_mode_specific_connection_count(psoc, mode, NULL) > 0) { + /* Check, store and temp delete the mode's parameter */ + policy_mgr_store_and_del_conn_info(psoc, mode, false, + info, &num_cxn_del); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + /* Set the PCL to the FW since connection got updated */ + policy_mgr_pdev_set_pcl(psoc, pcl_mode); + policy_mgr_debug("Set PCL to FW for mode:%d", mode); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + /* Restore the connection info */ + policy_mgr_restore_deleted_conn_info(psoc, info, num_cxn_del); + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); +} + +static uint32_t pm_get_vdev_id_of_first_conn_idx(struct wlan_objmgr_psoc *psoc) +{ + uint32_t conn_index = 0, vdev_id = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return conn_index; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if (pm_conc_connection_list[conn_index].in_use) { + vdev_id = pm_conc_connection_list[conn_index].vdev_id; + break; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + if (conn_index == MAX_NUMBER_OF_CONC_CONNECTIONS) + policy_mgr_debug("Use default vdev_id:%d for opportunistic upgrade", + vdev_id); + else + policy_mgr_debug("Use vdev_id:%d for opportunistic upgrade", + vdev_id); + + return vdev_id; +} + +/** + * pm_dbs_opportunistic_timer_handler() - handler of + * dbs_opportunistic_timer + * @data: context + * + * handler for dbs_opportunistic_timer + * + * Return: None + */ +void pm_dbs_opportunistic_timer_handler(void *data) +{ + enum policy_mgr_conc_next_action action = PM_NOP; + uint32_t session_id; + struct wlan_objmgr_psoc *psoc = (struct wlan_objmgr_psoc *)data; + enum policy_mgr_conn_update_reason reason; + + if (!psoc) { + policy_mgr_err("Invalid Context"); + return; + } + + /* if we still need it */ + action = policy_mgr_need_opportunistic_upgrade(psoc, &reason); + policy_mgr_debug("action:%d", action); + if (!action) + return; + session_id = pm_get_vdev_id_of_first_conn_idx(psoc); + policy_mgr_next_actions(psoc, session_id, action, + reason); +} + +/** + * policy_mgr_get_connection_for_vdev_id() - provides the + * perticular connection with the requested vdev id + * @vdev_id: vdev id of the connection + * + * This function provides the specific connection with the + * requested vdev id + * + * Return: index in the connection table + */ +static uint32_t policy_mgr_get_connection_for_vdev_id( + struct wlan_objmgr_psoc *psoc, uint32_t vdev_id) +{ + uint32_t conn_index = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return conn_index; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if ((pm_conc_connection_list[conn_index].vdev_id == vdev_id) && + pm_conc_connection_list[conn_index].in_use) { + break; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return conn_index; +} + +/** + * policy_mgr_get_mode() - Get mode from type and subtype + * @type: type + * @subtype: subtype + * + * Get the concurrency mode from the type and subtype + * of the interface + * + * Return: policy_mgr_con_mode + */ +enum policy_mgr_con_mode policy_mgr_get_mode(uint8_t type, + uint8_t subtype) +{ + enum policy_mgr_con_mode mode = PM_MAX_NUM_OF_MODE; + + if (type == WMI_VDEV_TYPE_AP) { + switch (subtype) { + case 0: + mode = PM_SAP_MODE; + break; + case WMI_UNIFIED_VDEV_SUBTYPE_P2P_GO: + mode = PM_P2P_GO_MODE; + break; + default: + policy_mgr_err("Unknown subtype %d for type %d", + subtype, type); + break; + } + } else if (type == WMI_VDEV_TYPE_STA) { + switch (subtype) { + case 0: + mode = PM_STA_MODE; + break; + case WMI_UNIFIED_VDEV_SUBTYPE_P2P_CLIENT: + mode = PM_P2P_CLIENT_MODE; + break; + default: + policy_mgr_err("Unknown subtype %d for type %d", + subtype, type); + break; + } + } else if (type == WMI_VDEV_TYPE_IBSS) { + mode = PM_IBSS_MODE; + } else { + policy_mgr_err("Unknown type %d", type); + } + + return mode; +} + +/** + * policy_mgr_get_bw() - Get channel bandwidth type used by WMI + * @chan_width: channel bandwidth type defined by host + * + * Get the channel bandwidth type used by WMI + * + * Return: hw_mode_bandwidth + */ +enum hw_mode_bandwidth policy_mgr_get_bw(enum phy_ch_width chan_width) +{ + enum hw_mode_bandwidth bw = HW_MODE_BW_NONE; + + switch (chan_width) { + case CH_WIDTH_20MHZ: + bw = HW_MODE_20_MHZ; + break; + case CH_WIDTH_40MHZ: + bw = HW_MODE_40_MHZ; + break; + case CH_WIDTH_80MHZ: + bw = HW_MODE_80_MHZ; + break; + case CH_WIDTH_160MHZ: + bw = HW_MODE_160_MHZ; + break; + case CH_WIDTH_80P80MHZ: + bw = HW_MODE_80_PLUS_80_MHZ; + break; + case CH_WIDTH_5MHZ: + bw = HW_MODE_5_MHZ; + break; + case CH_WIDTH_10MHZ: + bw = HW_MODE_10_MHZ; + break; + default: + policy_mgr_err("Unknown channel BW type %d", chan_width); + break; + } + + return bw; +} + +/** + * policy_mgr_get_sbs_channels() - provides the sbs channel(s) + * with respect to current connection(s) + * @channels: the channel(s) on which current connection(s) is + * @len: Number of channels + * @pcl_weight: Pointer to the weights of PCL + * @weight_len: Max length of the weight list + * @index: Index from which the weight list needs to be populated + * @group_id: Next available groups for weight assignment + * @available_5g_channels: List of available 5g channels + * @available_5g_channels_len: Length of the 5g channels list + * @add_5g_channels: If this flag is true append 5G channel list as well + * + * This function provides the channel(s) on which current + * connection(s) is/are + * + * Return: QDF_STATUS + */ + +static QDF_STATUS policy_mgr_get_sbs_channels(uint8_t *channels, + uint32_t *len, uint8_t *pcl_weight, uint32_t weight_len, + uint32_t *index, enum policy_mgr_pcl_group_id group_id, + uint8_t *available_5g_channels, + uint32_t available_5g_channels_len, + bool add_5g_channels) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t conn_index = 0, num_channels = 0; + uint32_t num_5g_channels = 0, cur_5g_channel = 0; + uint8_t remaining_5g_Channels[QDF_MAX_NUM_CHAN] = {}; + uint32_t remaining_channel_index = 0; + uint32_t j = 0, i = 0, weight1, weight2; + + if ((NULL == channels) || (NULL == len)) { + policy_mgr_err("channels or len is NULL"); + status = QDF_STATUS_E_FAILURE; + return status; + } + + if (group_id == POLICY_MGR_PCL_GROUP_ID1_ID2) { + weight1 = WEIGHT_OF_GROUP1_PCL_CHANNELS; + weight2 = WEIGHT_OF_GROUP2_PCL_CHANNELS; + } else if (group_id == POLICY_MGR_PCL_GROUP_ID2_ID3) { + weight1 = WEIGHT_OF_GROUP2_PCL_CHANNELS; + weight2 = WEIGHT_OF_GROUP3_PCL_CHANNELS; + } else { + weight1 = WEIGHT_OF_GROUP3_PCL_CHANNELS; + weight2 = WEIGHT_OF_GROUP4_PCL_CHANNELS; + } + + policy_mgr_debug("weight1=%d weight2=%d index=%d ", + weight1, weight2, *index); + + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if ((WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[conn_index].chan)) + && (pm_conc_connection_list[conn_index].in_use)) { + num_5g_channels++; + cur_5g_channel = + pm_conc_connection_list[conn_index].chan; + } + conn_index++; + } + + conn_index = 0; + if (num_5g_channels > 1) { + /* This case we are already in SBS so return the channels */ + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + channels[num_channels++] = + pm_conc_connection_list[conn_index++].chan; + if (*index < weight_len) + pcl_weight[(*index)++] = weight1; + } + *len = num_channels; + /* fix duplicate issue later */ + if (add_5g_channels) + for (j = 0; j < available_5g_channels_len; j++) + remaining_5g_Channels[ + remaining_channel_index++] = + available_5g_channels[j]; + } else { + /* Get list of valid sbs channels for the current + * connected channel + */ + for (j = 0; j < available_5g_channels_len; j++) { + if (WLAN_REG_IS_CHANNEL_VALID_5G_SBS( + cur_5g_channel, available_5g_channels[j])) { + channels[num_channels++] = + available_5g_channels[j]; + } else { + remaining_5g_Channels[ + remaining_channel_index++] = + available_5g_channels[j]; + continue; + } + if (*index < weight_len) + pcl_weight[(*index)++] = weight1; + } + *len = num_channels; + } + + if (add_5g_channels) { + qdf_mem_copy(channels+num_channels, remaining_5g_Channels, + remaining_channel_index); + *len += remaining_channel_index; + for (i = 0; ((i < remaining_channel_index) + && (i < weight_len)); i++) + pcl_weight[i] = weight2; + } + + return status; +} + + +/** + * policy_mgr_get_connection_channels() - provides the channel(s) + * on which current connection(s) is + * @channels: the channel(s) on which current connection(s) is + * @len: Number of channels + * @order: no order OR 2.4 Ghz channel followed by 5 Ghz + * channel OR 5 Ghz channel followed by 2.4 Ghz channel + * @skip_dfs_channel: if this flag is true then skip the dfs channel + * @pcl_weight: Pointer to the weights of PCL + * @weight_len: Max length of the weight list + * @index: Index from which the weight list needs to be populated + * @group_id: Next available groups for weight assignment + * + * + * This function provides the channel(s) on which current + * connection(s) is/are + * + * Return: QDF_STATUS + */ +static +QDF_STATUS policy_mgr_get_connection_channels(struct wlan_objmgr_psoc *psoc, + uint8_t *channels, + uint32_t *len, enum policy_mgr_pcl_channel_order order, + bool skip_dfs_channel, + uint8_t *pcl_weight, uint32_t weight_len, + uint32_t *index, enum policy_mgr_pcl_group_id group_id) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint32_t conn_index = 0, num_channels = 0; + uint32_t weight1, weight2; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return status; + } + + if ((NULL == channels) || (NULL == len)) { + policy_mgr_err("channels or len is NULL"); + status = QDF_STATUS_E_FAILURE; + return status; + } + + /* POLICY_MGR_PCL_GROUP_ID1_ID2 indicates that all three weights are + * available for assignment. i.e., WEIGHT_OF_GROUP1_PCL_CHANNELS, + * WEIGHT_OF_GROUP2_PCL_CHANNELS and WEIGHT_OF_GROUP3_PCL_CHANNELS + * are all available. Since in this function only two weights are + * assigned at max, only group1 and group2 weights are considered. + * + * The other possible group id POLICY_MGR_PCL_GROUP_ID2_ID3 indicates + * group1 was assigned the weight WEIGHT_OF_GROUP1_PCL_CHANNELS and + * only weights WEIGHT_OF_GROUP2_PCL_CHANNELS and + * WEIGHT_OF_GROUP3_PCL_CHANNELS are available for further weight + * assignments. + * + * e.g., when order is POLICY_MGR_PCL_ORDER_24G_THEN_5G and group id is + * POLICY_MGR_PCL_GROUP_ID2_ID3, WEIGHT_OF_GROUP2_PCL_CHANNELS is + * assigned to 2.4GHz channels and the weight + * WEIGHT_OF_GROUP3_PCL_CHANNELS is assigned to the 5GHz channels. + */ + if (group_id == POLICY_MGR_PCL_GROUP_ID1_ID2) { + weight1 = WEIGHT_OF_GROUP1_PCL_CHANNELS; + weight2 = WEIGHT_OF_GROUP2_PCL_CHANNELS; + } else { + weight1 = WEIGHT_OF_GROUP2_PCL_CHANNELS; + weight2 = WEIGHT_OF_GROUP3_PCL_CHANNELS; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (POLICY_MGR_PCL_ORDER_NONE == order) { + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (skip_dfs_channel && wlan_reg_is_dfs_ch(pm_ctx->pdev, + pm_conc_connection_list[conn_index].chan)) { + conn_index++; + } else if (*index < weight_len) { + channels[num_channels++] = + pm_conc_connection_list[conn_index++].chan; + pcl_weight[(*index)++] = weight1; + } else { + conn_index++; + } + } + *len = num_channels; + } else if (POLICY_MGR_PCL_ORDER_24G_THEN_5G == order) { + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[conn_index].chan) + && (*index < weight_len)) { + channels[num_channels++] = + pm_conc_connection_list[conn_index++].chan; + pcl_weight[(*index)++] = weight1; + } else { + conn_index++; + } + } + conn_index = 0; + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (skip_dfs_channel && wlan_reg_is_dfs_ch(pm_ctx->pdev, + pm_conc_connection_list[conn_index].chan)) { + conn_index++; + } else if (WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[conn_index].chan) + && (*index < weight_len)) { + channels[num_channels++] = + pm_conc_connection_list[conn_index++].chan; + pcl_weight[(*index)++] = weight2; + } else { + conn_index++; + } + } + *len = num_channels; + } else if (POLICY_MGR_PCL_ORDER_5G_THEN_2G == order) { + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (skip_dfs_channel && wlan_reg_is_dfs_ch(pm_ctx->pdev, + pm_conc_connection_list[conn_index].chan)) { + conn_index++; + } else if (WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[conn_index].chan) + && (*index < weight_len)) { + channels[num_channels++] = + pm_conc_connection_list[conn_index++].chan; + pcl_weight[(*index)++] = weight1; + } else { + conn_index++; + } + } + conn_index = 0; + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[conn_index].chan) + && (*index < weight_len)) { + channels[num_channels++] = + pm_conc_connection_list[conn_index++].chan; + pcl_weight[(*index)++] = weight2; + + } else { + conn_index++; + } + } + *len = num_channels; + } else { + policy_mgr_err("unknown order %d", order); + status = QDF_STATUS_E_FAILURE; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return status; +} + +/** + * policy_mgr_set_weight_of_dfs_passive_channels_to_zero() - set weight of dfs + * and passive channels to 0 + * @psoc: pointer to soc + * @pcl_channels: preferred channel list + * @len: length of preferred channel list + * @weight_list: preferred channel weight list + * @weight_len: length of weight list + * This function set the weight of dfs and passive channels to 0 + * + * Return: None + */ +void policy_mgr_set_weight_of_dfs_passive_channels_to_zero( + struct wlan_objmgr_psoc *psoc, uint8_t *pcl_channels, + uint32_t *len, uint8_t *weight_list, uint32_t weight_len) +{ + uint8_t i; + uint32_t orig_channel_count = 0; + bool sta_sap_scc_on_dfs_chan; + uint32_t sap_count; + enum channel_state channel_state; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + sta_sap_scc_on_dfs_chan = + policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc); + sap_count = policy_mgr_mode_specific_connection_count(psoc, + PM_SAP_MODE, NULL); + policy_mgr_debug("sta_sap_scc_on_dfs_chan %u, sap_count %u", + sta_sap_scc_on_dfs_chan, sap_count); + + if (!sta_sap_scc_on_dfs_chan || !sap_count) + return; + + if (len) + orig_channel_count = QDF_MIN(*len, QDF_MAX_NUM_CHAN); + else { + policy_mgr_err("invalid number of channel length"); + return; + } + + policy_mgr_debug("Set weight of DFS/passive channels to 0"); + + for (i = 0; i < orig_channel_count; i++) { + channel_state = reg_get_channel_state(pm_ctx->pdev, + pcl_channels[i]); + if ((channel_state == CHANNEL_STATE_DISABLE) || + (channel_state == CHANNEL_STATE_INVALID)) + /* Set weight of inactive channels to 0 */ + weight_list[i] = 0; + + policy_mgr_debug("chan[%d] - %d, weight[%d] - %d", + i, pcl_channels[i], i, weight_list[i]); + } + + return; +} + +/** + * policy_mgr_get_channel_list() - provides the channel list + * suggestion for new connection + * @pcl: The preferred channel list enum + * @pcl_channels: PCL channels + * @len: length of the PCL + * @mode: concurrency mode for which channel list is requested + * @pcl_weights: Weights of the PCL + * @weight_len: Max length of the weight list + * + * This function provides the actual channel list based on the + * current regulatory domain derived using preferred channel + * list enum obtained from one of the pcl_table + * + * Return: Channel List + */ +QDF_STATUS policy_mgr_get_channel_list(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_pcl_type pcl, + uint8_t *pcl_channels, uint32_t *len, + enum policy_mgr_con_mode mode, + uint8_t *pcl_weights, uint32_t weight_len) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t num_channels = 0; + uint32_t sbs_num_channels = 0; + uint32_t chan_index = 0, chan_index_24 = 0, chan_index_5 = 0; + uint8_t channel_list[QDF_MAX_NUM_CHAN] = {0}; + uint8_t channel_list_24[QDF_MAX_NUM_CHAN] = {0}; + uint8_t channel_list_5[QDF_MAX_NUM_CHAN] = {0}; + uint8_t sbs_channel_list[QDF_MAX_NUM_CHAN] = {0}; + bool skip_dfs_channel = false; + bool is_etsi13_srd_chan_allowed_in_mas_mode = true; + uint32_t i = 0, j = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool sta_sap_scc_on_dfs_chan; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return status; + } + + if ((NULL == pcl_channels) || (NULL == len)) { + policy_mgr_err("pcl_channels or len is NULL"); + return status; + } + + if (PM_MAX_PCL_TYPE == pcl) { + /* msg */ + policy_mgr_err("pcl is invalid"); + return status; + } + + if (PM_NONE == pcl) { + /* msg */ + policy_mgr_debug("pcl is 0"); + return QDF_STATUS_SUCCESS; + } + /* get the channel list for current domain */ + status = policy_mgr_get_valid_chans(psoc, channel_list, &num_channels); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Error in getting valid channels"); + return status; + } + + /* + * if you have atleast one STA connection then don't fill DFS channels + * in the preferred channel list + */ + sta_sap_scc_on_dfs_chan = + policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc); + policy_mgr_debug("sta_sap_scc_on_dfs_chan %u", sta_sap_scc_on_dfs_chan); + if ((mode == PM_SAP_MODE) || (mode == PM_P2P_GO_MODE)) { + if ((policy_mgr_mode_specific_connection_count(psoc, + PM_STA_MODE, + NULL) > 0) && + (!sta_sap_scc_on_dfs_chan)) { + policy_mgr_debug("skip DFS ch from pcl for SAP/Go"); + skip_dfs_channel = true; + } + is_etsi13_srd_chan_allowed_in_mas_mode = + wlan_reg_is_etsi13_srd_chan_allowed_master_mode(pm_ctx-> + pdev); + } + + /* Let's divide the list in 2.4 & 5 Ghz lists */ + while ((chan_index < QDF_MAX_NUM_CHAN) && + (channel_list[chan_index] <= 11) && + (chan_index_24 < QDF_MAX_NUM_CHAN)) + channel_list_24[chan_index_24++] = channel_list[chan_index++]; + if ((chan_index < QDF_MAX_NUM_CHAN) && + (channel_list[chan_index] == 12) && + (chan_index_24 < QDF_MAX_NUM_CHAN)) { + channel_list_24[chan_index_24++] = channel_list[chan_index++]; + if ((chan_index < QDF_MAX_NUM_CHAN) && + (channel_list[chan_index] == 13) && + (chan_index_24 < QDF_MAX_NUM_CHAN)) { + channel_list_24[chan_index_24++] = + channel_list[chan_index++]; + if ((chan_index < QDF_MAX_NUM_CHAN) && + (channel_list[chan_index] == 14) && + (chan_index_24 < QDF_MAX_NUM_CHAN)) + channel_list_24[chan_index_24++] = + channel_list[chan_index++]; + } + } + + while ((chan_index < num_channels) && + (chan_index < QDF_MAX_NUM_CHAN) && + (chan_index_5 < QDF_MAX_NUM_CHAN)) { + if ((true == skip_dfs_channel) && + wlan_reg_is_dfs_ch(pm_ctx->pdev, + channel_list[chan_index])) { + chan_index++; + continue; + } + if (!is_etsi13_srd_chan_allowed_in_mas_mode && + wlan_reg_is_etsi13_srd_chan(pm_ctx->pdev, + channel_list[chan_index])) { + chan_index++; + continue; + } + channel_list_5[chan_index_5++] = channel_list[chan_index++]; + } + + num_channels = 0; + sbs_num_channels = 0; + /* In the below switch case, the channel list is populated based on the + * pcl. e.g., if the pcl is PM_SCC_CH_24G, the SCC channel group is + * populated first followed by the 2.4GHz channel group. Along with + * this, the weights are also populated in the same order for each of + * these groups. There are three weight groups: + * WEIGHT_OF_GROUP1_PCL_CHANNELS, WEIGHT_OF_GROUP2_PCL_CHANNELS and + * WEIGHT_OF_GROUP3_PCL_CHANNELS. + * + * e.g., if pcl is PM_SCC_ON_5_SCC_ON_24_24G: scc on 5GHz (group1) + * channels take the weight WEIGHT_OF_GROUP1_PCL_CHANNELS, scc on 2.4GHz + * (group2) channels take the weight WEIGHT_OF_GROUP2_PCL_CHANNELS and + * 2.4GHz (group3) channels take the weight + * WEIGHT_OF_GROUP3_PCL_CHANNELS. + * + * When the weight to be assigned to the group is known along with the + * number of channels, the weights are directly assigned to the + * pcl_weights list. But, the channel list is populated using + * policy_mgr_get_connection_channels(), the order of weights to be used + * is passed as an argument to the function + * policy_mgr_get_connection_channels() using + * 'enum policy_mgr_pcl_group_id' which indicates the next available + * weights to be used and policy_mgr_get_connection_channels() will take + * care of the weight assignments. + * + * e.g., 'enum policy_mgr_pcl_group_id' value of + * POLICY_MGR_PCL_GROUP_ID2_ID3 indicates that the next available groups + * for weight assignment are WEIGHT_OF_GROUP2_PCL_CHANNELS and + * WEIGHT_OF_GROUP3_PCL_CHANNELS and that the + * weight WEIGHT_OF_GROUP1_PCL_CHANNELS was already allocated. + * So, in the same example, when order is + * POLICY_MGR_PCL_ORDER_24G_THEN_5G, + * policy_mgr_get_connection_channels() will assign the weight + * WEIGHT_OF_GROUP2_PCL_CHANNELS to 2.4GHz channels and assign the + * weight WEIGHT_OF_GROUP3_PCL_CHANNELS to 5GHz channels. + */ + switch (pcl) { + case PM_24G: + chan_index_24 = QDF_MIN(chan_index_24, weight_len); + qdf_mem_copy(pcl_channels, channel_list_24, + chan_index_24); + *len = chan_index_24; + for (i = 0; i < *len; i++) + pcl_weights[i] = WEIGHT_OF_GROUP1_PCL_CHANNELS; + status = QDF_STATUS_SUCCESS; + break; + case PM_5G: + chan_index_5 = QDF_MIN(chan_index_5, weight_len); + qdf_mem_copy(pcl_channels, channel_list_5, + chan_index_5); + *len = chan_index_5; + for (i = 0; i < *len; i++) + pcl_weights[i] = WEIGHT_OF_GROUP1_PCL_CHANNELS; + status = QDF_STATUS_SUCCESS; + break; + case PM_SCC_CH: + case PM_MCC_CH: + policy_mgr_get_connection_channels(psoc, + channel_list, &num_channels, POLICY_MGR_PCL_ORDER_NONE, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID1_ID2); + qdf_mem_copy(pcl_channels, channel_list, num_channels); + *len = num_channels; + status = QDF_STATUS_SUCCESS; + break; + case PM_SCC_CH_24G: + case PM_MCC_CH_24G: + policy_mgr_get_connection_channels(psoc, + channel_list, &num_channels, POLICY_MGR_PCL_ORDER_NONE, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID1_ID2); + qdf_mem_copy(pcl_channels, channel_list, num_channels); + *len = num_channels; + chan_index_24 = QDF_MIN((num_channels + chan_index_24), + weight_len) - num_channels; + qdf_mem_copy(&pcl_channels[num_channels], + channel_list_24, chan_index_24); + *len += chan_index_24; + for (j = 0; j < chan_index_24; i++, j++) + pcl_weights[i] = WEIGHT_OF_GROUP2_PCL_CHANNELS; + + status = QDF_STATUS_SUCCESS; + break; + case PM_SCC_CH_5G: + case PM_MCC_CH_5G: + policy_mgr_get_connection_channels(psoc, + channel_list, &num_channels, POLICY_MGR_PCL_ORDER_NONE, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID1_ID2); + qdf_mem_copy(pcl_channels, channel_list, + num_channels); + *len = num_channels; + chan_index_5 = QDF_MIN((num_channels + chan_index_5), + weight_len) - num_channels; + qdf_mem_copy(&pcl_channels[num_channels], + channel_list_5, chan_index_5); + *len += chan_index_5; + for (j = 0; j < chan_index_5; i++, j++) + pcl_weights[i] = WEIGHT_OF_GROUP2_PCL_CHANNELS; + status = QDF_STATUS_SUCCESS; + break; + case PM_24G_SCC_CH: + case PM_24G_MCC_CH: + chan_index_24 = QDF_MIN(chan_index_24, weight_len); + qdf_mem_copy(pcl_channels, channel_list_24, + chan_index_24); + *len = chan_index_24; + for (i = 0; i < chan_index_24; i++) + pcl_weights[i] = WEIGHT_OF_GROUP1_PCL_CHANNELS; + policy_mgr_get_connection_channels(psoc, + channel_list, &num_channels, POLICY_MGR_PCL_ORDER_NONE, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID2_ID3); + qdf_mem_copy(&pcl_channels[chan_index_24], + channel_list, num_channels); + *len += num_channels; + status = QDF_STATUS_SUCCESS; + break; + case PM_5G_SCC_CH: + case PM_5G_MCC_CH: + chan_index_5 = QDF_MIN(chan_index_5, weight_len); + qdf_mem_copy(pcl_channels, channel_list_5, + chan_index_5); + *len = chan_index_5; + for (i = 0; i < chan_index_5; i++) + pcl_weights[i] = WEIGHT_OF_GROUP1_PCL_CHANNELS; + policy_mgr_get_connection_channels(psoc, + channel_list, &num_channels, POLICY_MGR_PCL_ORDER_NONE, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID2_ID3); + qdf_mem_copy(&pcl_channels[chan_index_5], + channel_list, num_channels); + *len += num_channels; + status = QDF_STATUS_SUCCESS; + break; + case PM_SCC_ON_24_SCC_ON_5: + policy_mgr_get_connection_channels(psoc, + channel_list, &num_channels, + POLICY_MGR_PCL_ORDER_24G_THEN_5G, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID1_ID2); + qdf_mem_copy(pcl_channels, channel_list, + num_channels); + *len = num_channels; + status = QDF_STATUS_SUCCESS; + break; + case PM_SCC_ON_5_SCC_ON_24: + policy_mgr_get_connection_channels(psoc, + channel_list, &num_channels, + POLICY_MGR_PCL_ORDER_5G_THEN_2G, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID1_ID2); + qdf_mem_copy(pcl_channels, channel_list, num_channels); + *len = num_channels; + status = QDF_STATUS_SUCCESS; + break; + case PM_SCC_ON_24_SCC_ON_5_24G: + policy_mgr_get_connection_channels(psoc, + channel_list, &num_channels, + POLICY_MGR_PCL_ORDER_24G_THEN_5G, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID1_ID2); + qdf_mem_copy(pcl_channels, channel_list, num_channels); + *len = num_channels; + chan_index_24 = QDF_MIN((num_channels + chan_index_24), + weight_len) - num_channels; + qdf_mem_copy(&pcl_channels[num_channels], + channel_list_24, chan_index_24); + *len += chan_index_24; + for (j = 0; j < chan_index_24; i++, j++) + pcl_weights[i] = WEIGHT_OF_GROUP3_PCL_CHANNELS; + status = QDF_STATUS_SUCCESS; + break; + case PM_SCC_ON_24_SCC_ON_5_5G: + policy_mgr_get_connection_channels(psoc, + channel_list, &num_channels, + POLICY_MGR_PCL_ORDER_24G_THEN_5G, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID1_ID2); + qdf_mem_copy(pcl_channels, channel_list, num_channels); + *len = num_channels; + chan_index_5 = QDF_MIN((num_channels + chan_index_5), + weight_len) - num_channels; + qdf_mem_copy(&pcl_channels[num_channels], + channel_list_5, chan_index_5); + *len += chan_index_5; + for (j = 0; j < chan_index_5; i++, j++) + pcl_weights[i] = WEIGHT_OF_GROUP3_PCL_CHANNELS; + status = QDF_STATUS_SUCCESS; + break; + case PM_SCC_ON_5_SCC_ON_24_24G: + policy_mgr_get_connection_channels(psoc, + channel_list, &num_channels, + POLICY_MGR_PCL_ORDER_5G_THEN_2G, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID1_ID2); + qdf_mem_copy(pcl_channels, channel_list, num_channels); + *len = num_channels; + chan_index_24 = QDF_MIN((num_channels + chan_index_24), + weight_len) - num_channels; + qdf_mem_copy(&pcl_channels[num_channels], + channel_list_24, chan_index_24); + *len += chan_index_24; + for (j = 0; j < chan_index_24; i++, j++) + pcl_weights[i] = WEIGHT_OF_GROUP3_PCL_CHANNELS; + status = QDF_STATUS_SUCCESS; + break; + case PM_SCC_ON_5_SCC_ON_24_5G: + policy_mgr_get_connection_channels(psoc, + channel_list, &num_channels, + POLICY_MGR_PCL_ORDER_5G_THEN_2G, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID1_ID2); + qdf_mem_copy(pcl_channels, channel_list, num_channels); + *len = num_channels; + chan_index_5 = QDF_MIN((num_channels + chan_index_5), + weight_len) - num_channels; + qdf_mem_copy(&pcl_channels[num_channels], + channel_list_5, chan_index_5); + *len += chan_index_5; + for (j = 0; j < chan_index_5; i++, j++) + pcl_weights[i] = WEIGHT_OF_GROUP3_PCL_CHANNELS; + status = QDF_STATUS_SUCCESS; + break; + case PM_24G_SCC_CH_SBS_CH: + qdf_mem_copy(pcl_channels, channel_list_24, + chan_index_24); + *len = chan_index_24; + for (i = 0; ((i < chan_index_24) && (i < weight_len)); i++) + pcl_weights[i] = WEIGHT_OF_GROUP1_PCL_CHANNELS; + policy_mgr_get_connection_channels(psoc, + channel_list, &num_channels, POLICY_MGR_PCL_ORDER_NONE, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID2_ID3); + qdf_mem_copy(&pcl_channels[chan_index_24], + channel_list, num_channels); + *len += num_channels; + if (policy_mgr_is_hw_sbs_capable(psoc)) { + policy_mgr_get_sbs_channels( + sbs_channel_list, &sbs_num_channels, pcl_weights, + weight_len, &i, POLICY_MGR_PCL_GROUP_ID3_ID4, + channel_list_5, chan_index_5, false); + qdf_mem_copy( + &pcl_channels[chan_index_24 + num_channels], + sbs_channel_list, sbs_num_channels); + *len += sbs_num_channels; + } + status = QDF_STATUS_SUCCESS; + break; + case PM_24G_SCC_CH_SBS_CH_5G: + qdf_mem_copy(pcl_channels, channel_list_24, + chan_index_24); + *len = chan_index_24; + for (i = 0; ((i < chan_index_24) && (i < weight_len)); i++) + pcl_weights[i] = WEIGHT_OF_GROUP1_PCL_CHANNELS; + policy_mgr_get_connection_channels(psoc, + channel_list, &num_channels, POLICY_MGR_PCL_ORDER_NONE, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID2_ID3); + qdf_mem_copy(&pcl_channels[chan_index_24], + channel_list, num_channels); + *len += num_channels; + if (policy_mgr_is_hw_sbs_capable(psoc)) { + policy_mgr_get_sbs_channels( + sbs_channel_list, &sbs_num_channels, pcl_weights, + weight_len, &i, POLICY_MGR_PCL_GROUP_ID3_ID4, + channel_list_5, chan_index_5, true); + qdf_mem_copy( + &pcl_channels[chan_index_24 + num_channels], + sbs_channel_list, sbs_num_channels); + *len += sbs_num_channels; + } else { + qdf_mem_copy( + &pcl_channels[chan_index_24 + num_channels], + channel_list_5, chan_index_5); + *len += chan_index_5; + for (i = chan_index_24 + num_channels; + ((i < *len) && (i < weight_len)); i++) + pcl_weights[i] = WEIGHT_OF_GROUP3_PCL_CHANNELS; + } + status = QDF_STATUS_SUCCESS; + break; + case PM_24G_SBS_CH_MCC_CH: + qdf_mem_copy(pcl_channels, channel_list_24, + chan_index_24); + *len = chan_index_24; + for (i = 0; ((i < chan_index_24) && (i < weight_len)); i++) + pcl_weights[i] = WEIGHT_OF_GROUP1_PCL_CHANNELS; + if (policy_mgr_is_hw_sbs_capable(psoc)) { + policy_mgr_get_sbs_channels( + sbs_channel_list, &sbs_num_channels, pcl_weights, + weight_len, &i, POLICY_MGR_PCL_GROUP_ID2_ID3, + channel_list_5, chan_index_5, false); + qdf_mem_copy(&pcl_channels[num_channels], + sbs_channel_list, sbs_num_channels); + *len += sbs_num_channels; + } + policy_mgr_get_connection_channels(psoc, + channel_list, &num_channels, POLICY_MGR_PCL_ORDER_NONE, + skip_dfs_channel, pcl_weights, weight_len, &i, + POLICY_MGR_PCL_GROUP_ID2_ID3); + qdf_mem_copy(&pcl_channels[chan_index_24], + channel_list, num_channels); + *len += num_channels; + status = QDF_STATUS_SUCCESS; + break; + case PM_SBS_CH_5G: + if (policy_mgr_is_hw_sbs_capable(psoc)) { + policy_mgr_get_sbs_channels( + sbs_channel_list, &sbs_num_channels, pcl_weights, + weight_len, &i, POLICY_MGR_PCL_GROUP_ID1_ID2, + channel_list_5, chan_index_5, true); + qdf_mem_copy(&pcl_channels[num_channels], + sbs_channel_list, sbs_num_channels); + *len += sbs_num_channels; + } else { + qdf_mem_copy(pcl_channels, channel_list_5, + chan_index_5); + *len = chan_index_5; + for (i = 0; ((i < *len) && (i < weight_len)); i++) + pcl_weights[i] = WEIGHT_OF_GROUP1_PCL_CHANNELS; + } + status = QDF_STATUS_SUCCESS; + break; + default: + policy_mgr_err("unknown pcl value %d", pcl); + break; + } + + if ((*len != 0) && (*len != i)) + policy_mgr_debug("pcl len (%d) and weight list len mismatch (%d)", + *len, i); + + /* check the channel avoidance list for beaconing entities */ + if ((mode == PM_SAP_MODE) || (mode == PM_P2P_GO_MODE)) + policy_mgr_update_with_safe_channel_list(psoc, pcl_channels, + len, pcl_weights, + weight_len); + + policy_mgr_set_weight_of_dfs_passive_channels_to_zero(psoc, + pcl_channels, len, pcl_weights, weight_len); + return status; +} + +/** + * policy_mgr_disallow_mcc() - Check for mcc + * + * @channel: channel on which new connection is coming up + * + * When a new connection is about to come up check if current + * concurrency combination including the new connection is + * causing MCC + * + * Return: True/False + */ +bool policy_mgr_disallow_mcc(struct wlan_objmgr_psoc *psoc, + uint8_t channel) +{ + uint32_t index = 0; + bool match = false; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return match; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(index)) { + if (policy_mgr_is_hw_dbs_capable(psoc) == false) { + if (pm_conc_connection_list[index].chan != + channel) { + match = true; + break; + } + } else if (WLAN_REG_IS_5GHZ_CH + (pm_conc_connection_list[index].chan)) { + if (pm_conc_connection_list[index].chan != channel) { + match = true; + break; + } + } + index++; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return match; +} + +/** + * policy_mgr_allow_new_home_channel() - Check for allowed number of + * home channels + * @channel: channel on which new connection is coming up + * @num_connections: number of current connections + * + * When a new connection is about to come up check if current + * concurrency combination including the new connection is + * allowed or not based on the HW capability + * + * Return: True/False + */ +bool policy_mgr_allow_new_home_channel(struct wlan_objmgr_psoc *psoc, + uint8_t channel, uint32_t num_connections) +{ + bool status = true; + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t mcc_to_scc_switch; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + mcc_to_scc_switch = + policy_mgr_get_mcc_to_scc_switch_mode(psoc); + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (num_connections == 2) { + /* No SCC or MCC combination is allowed with / on DFS channel */ + if ((mcc_to_scc_switch == + QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION) + && wlan_reg_is_dfs_ch(pm_ctx->pdev, channel) && + (wlan_reg_is_dfs_ch(pm_ctx->pdev, + pm_conc_connection_list[0].chan) || + wlan_reg_is_dfs_ch(pm_ctx->pdev, + pm_conc_connection_list[1].chan))) { + + policy_mgr_err("Existing DFS connection, new 3-port DFS connection is not allowed"); + status = false; + + } else if (((pm_conc_connection_list[0].chan != + pm_conc_connection_list[1].chan) + || (mcc_to_scc_switch == + QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION) + ) && (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac)) { + if (policy_mgr_is_hw_dbs_capable(psoc) == false) { + if ((channel != + pm_conc_connection_list[0].chan) && + (channel != + pm_conc_connection_list[1].chan)) { + policy_mgr_err("don't allow 3rd home channel on same MAC"); + status = false; + } + } else if (((WLAN_REG_IS_24GHZ_CH(channel)) && + (WLAN_REG_IS_24GHZ_CH + (pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_24GHZ_CH + (pm_conc_connection_list[1].chan))) || + ((WLAN_REG_IS_5GHZ_CH(channel)) && + (WLAN_REG_IS_5GHZ_CH + (pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_5GHZ_CH + (pm_conc_connection_list[1].chan)))) { + policy_mgr_err("don't allow 3rd home channel on same MAC"); + status = false; + } + } + } else if ((num_connections == 1) + && (mcc_to_scc_switch == + QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION) + && wlan_reg_is_dfs_ch(pm_ctx->pdev, channel) + && wlan_reg_is_dfs_ch(pm_ctx->pdev, + pm_conc_connection_list[0].chan)) { + + policy_mgr_err("Existing DFS connection, new 2-port DFS connection is not allowed"); + status = false; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return status; +} + +/** + * policy_mgr_is_5g_channel_allowed() - check if 5g channel is allowed + * @channel: channel number which needs to be validated + * @list: list of existing connections. + * @mode: mode against which channel needs to be validated + * + * This API takes the channel as input and compares with existing + * connection channels. If existing connection's channel is DFS channel + * and provided channel is 5G channel then don't allow concurrency to + * happen as MCC with DFS channel is not yet supported + * + * Return: true if 5G channel is allowed, false if not allowed + * + */ +bool policy_mgr_is_5g_channel_allowed(struct wlan_objmgr_psoc *psoc, + uint8_t channel, uint32_t *list, + enum policy_mgr_con_mode mode) +{ + uint32_t index = 0, count = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + count = policy_mgr_mode_specific_connection_count(psoc, mode, list); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + while (index < count) { + if (wlan_reg_is_dfs_ch( + pm_ctx->pdev, + pm_conc_connection_list[list[index]].chan) && + WLAN_REG_IS_5GHZ_CH(channel) && + (channel != pm_conc_connection_list[list[index]].chan)) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_err("don't allow MCC if SAP/GO on DFS channel"); + return false; + } + index++; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return true; +} + +/** + * policy_mgr_nss_update_cb() - callback from SME confirming nss + * update + * @hdd_ctx: HDD Context + * @tx_status: tx completion status for updated beacon with new + * nss value + * @vdev_id: vdev id for the specific connection + * @next_action: next action to happen at policy mgr after + * beacon update + * @reason: Reason for nss update + * @original_vdev_id: original request hwmode change vdev id + * + * This function is the callback registered with SME at nss + * update request time + * + * Return: None + */ +static void policy_mgr_nss_update_cb(struct wlan_objmgr_psoc *psoc, + uint8_t tx_status, + uint8_t vdev_id, + uint8_t next_action, + enum policy_mgr_conn_update_reason reason, + uint32_t original_vdev_id) +{ + uint32_t conn_index = 0; + QDF_STATUS ret; + + if (QDF_STATUS_SUCCESS != tx_status) + policy_mgr_err("nss update failed(%d) for vdev %d", + tx_status, vdev_id); + + /* + * Check if we are ok to request for HW mode change now + */ + conn_index = policy_mgr_get_connection_for_vdev_id(psoc, vdev_id); + if (MAX_NUMBER_OF_CONC_CONNECTIONS == conn_index) { + policy_mgr_err("connection not found for vdev %d", vdev_id); + return; + } + + policy_mgr_debug("nss update successful for vdev:%d ori %d reason %d", + vdev_id, original_vdev_id, reason); + if (PM_NOP != next_action) + policy_mgr_next_actions(psoc, original_vdev_id, next_action, + reason); + else { + policy_mgr_debug("No action needed right now"); + ret = policy_mgr_set_opportunistic_update(psoc); + if (!QDF_IS_STATUS_SUCCESS(ret)) + policy_mgr_err("ERROR: set opportunistic_update event failed"); + } + + return; +} + +QDF_STATUS policy_mgr_nss_update(struct wlan_objmgr_psoc *psoc, + uint8_t new_nss, uint8_t next_action, + enum policy_mgr_band band, + enum policy_mgr_conn_update_reason reason, + uint32_t original_vdev_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t index, count; + uint32_t list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint32_t conn_index = 0; + uint32_t vdev_id; + uint32_t original_nss; + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint8_t chan; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return status; + } + + count = policy_mgr_mode_specific_connection_count(psoc, + PM_P2P_GO_MODE, list); + for (index = 0; index < count; index++) { + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + vdev_id = pm_conc_connection_list[list[index]].vdev_id; + original_nss = + pm_conc_connection_list[list[index]].original_nss; + chan = pm_conc_connection_list[list[index]].chan; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + conn_index = policy_mgr_get_connection_for_vdev_id( + psoc, vdev_id); + if (MAX_NUMBER_OF_CONC_CONNECTIONS == conn_index) { + policy_mgr_err("connection not found for vdev %d", + vdev_id); + continue; + } + + if (original_nss == 2 && + (band == POLICY_MGR_ANY || + (band == POLICY_MGR_BAND_24 && + WLAN_REG_IS_24GHZ_CH(chan)) || + (band == POLICY_MGR_BAND_5 && + WLAN_REG_IS_5GHZ_CH(chan)))) { + status = pm_ctx->sme_cbacks.sme_nss_update_request( + vdev_id, new_nss, + policy_mgr_nss_update_cb, + next_action, psoc, reason, + original_vdev_id); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("sme_nss_update_request() failed for vdev %d", + vdev_id); + } + } + } + + count = policy_mgr_mode_specific_connection_count(psoc, + PM_SAP_MODE, list); + for (index = 0; index < count; index++) { + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + vdev_id = pm_conc_connection_list[list[index]].vdev_id; + original_nss = + pm_conc_connection_list[list[index]].original_nss; + chan = pm_conc_connection_list[list[index]].chan; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + conn_index = policy_mgr_get_connection_for_vdev_id( + psoc, vdev_id); + if (MAX_NUMBER_OF_CONC_CONNECTIONS == conn_index) { + policy_mgr_err("connection not found for vdev %d", + vdev_id); + continue; + } + if (original_nss == 2 && + (band == POLICY_MGR_ANY || + (band == POLICY_MGR_BAND_24 && + WLAN_REG_IS_24GHZ_CH(chan)) || + (band == POLICY_MGR_BAND_5 && + WLAN_REG_IS_5GHZ_CH(chan)))) { + status = pm_ctx->sme_cbacks.sme_nss_update_request( + vdev_id, new_nss, + policy_mgr_nss_update_cb, + next_action, psoc, reason, + original_vdev_id); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("sme_nss_update_request() failed for vdev %d", + vdev_id); + } + } + } + + return status; +} + +/** + * policy_mgr_complete_action() - initiates actions needed on + * current connections once channel has been decided for the new + * connection + * @new_nss: the new nss value + * @next_action: next action to happen at policy mgr after + * beacon update + * @reason: Reason for connection update + * @session_id: Session id + * + * This function initiates initiates actions + * needed on current connections once channel has been decided + * for the new connection. Notifies UMAC & FW as well + * + * Return: QDF_STATUS enum + */ +QDF_STATUS policy_mgr_complete_action(struct wlan_objmgr_psoc *psoc, + uint8_t new_nss, uint8_t next_action, + enum policy_mgr_conn_update_reason reason, + uint32_t session_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + enum policy_mgr_band downgrade_band; + + if (policy_mgr_is_hw_dbs_capable(psoc) == false) { + policy_mgr_err("driver isn't dbs capable, no further action needed"); + return QDF_STATUS_E_NOSUPPORT; + } + + /* policy_mgr_complete_action() is called by policy_mgr_next_actions(). + * All other callers of policy_mgr_next_actions() have taken mutex + * protection. So, not taking any lock inside + * policy_mgr_complete_action() during pm_conc_connection_list access. + */ + if (next_action == PM_DBS1) + downgrade_band = POLICY_MGR_BAND_24; + else if (next_action == PM_DBS2) + downgrade_band = POLICY_MGR_BAND_5; + else + downgrade_band = POLICY_MGR_ANY; + + status = policy_mgr_nss_update(psoc, new_nss, next_action, + downgrade_band, reason, + session_id); + if (!QDF_IS_STATUS_SUCCESS(status)) + status = policy_mgr_next_actions(psoc, session_id, + next_action, reason); + + return status; +} + +enum policy_mgr_con_mode policy_mgr_get_mode_by_vdev_id( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id) +{ + enum policy_mgr_con_mode mode = PM_MAX_NUM_OF_MODE; + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t conn_index; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return mode; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) + if ((pm_conc_connection_list[conn_index].vdev_id == vdev_id) && + pm_conc_connection_list[conn_index].in_use){ + mode = pm_conc_connection_list[conn_index].mode; + break; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return mode; +} + +/** + * policy_mgr_init_connection_update() - Initialize connection + * update event + * @pm_ctx: policy mgr context + * + * Initializes the concurrent connection update event + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_init_connection_update( + struct policy_mgr_psoc_priv_obj *pm_ctx) +{ + QDF_STATUS qdf_status; + + qdf_status = qdf_event_create(&pm_ctx->connection_update_done_evt); + + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + policy_mgr_err("init event failed"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_get_current_pref_hw_mode_dbs_2x2() - Get the + * current preferred hw mode + * + * Get the preferred hw mode based on the current connection combinations + * + * Return: No change (PM_NOP), MCC (PM_SINGLE_MAC), + * DBS (PM_DBS), SBS (PM_SBS) + */ +enum policy_mgr_conc_next_action + policy_mgr_get_current_pref_hw_mode_dbs_2x2( + struct wlan_objmgr_psoc *psoc) +{ + uint32_t num_connections; + uint8_t band1, band2, band3; + struct policy_mgr_hw_mode_params hw_mode; + QDF_STATUS status; + + status = policy_mgr_get_current_hw_mode(psoc, &hw_mode); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("policy_mgr_get_current_hw_mode failed"); + return PM_NOP; + } + + num_connections = policy_mgr_get_connection_count(psoc); + + policy_mgr_debug("chan[0]:%d chan[1]:%d chan[2]:%d num_connections:%d dbs:%d", + pm_conc_connection_list[0].chan, + pm_conc_connection_list[1].chan, + pm_conc_connection_list[2].chan, num_connections, + hw_mode.dbs_cap); + + /* If the band of operation of both the MACs is the same, + * single MAC is preferred, otherwise DBS is preferred. + */ + switch (num_connections) { + case 1: + band1 = reg_chan_to_band(pm_conc_connection_list[0].chan); + if (band1 == BAND_2G) + return PM_DBS; + else + return PM_NOP; + case 2: + band1 = reg_chan_to_band(pm_conc_connection_list[0].chan); + band2 = reg_chan_to_band(pm_conc_connection_list[1].chan); + if ((band1 == BAND_2G) || + (band2 == BAND_2G)) { + if (!hw_mode.dbs_cap) + return PM_DBS; + else + return PM_NOP; + } else if ((band1 == BAND_5G) && + (band2 == BAND_5G)) { + if (WLAN_REG_IS_CHANNEL_VALID_5G_SBS( + pm_conc_connection_list[0].chan, + pm_conc_connection_list[1].chan)) { + if (!hw_mode.sbs_cap) + return PM_SBS; + else + return PM_NOP; + } else { + if (hw_mode.sbs_cap || hw_mode.dbs_cap) + return PM_SINGLE_MAC; + else + return PM_NOP; + } + } else + return PM_NOP; + case 3: + band1 = reg_chan_to_band(pm_conc_connection_list[0].chan); + band2 = reg_chan_to_band(pm_conc_connection_list[1].chan); + band3 = reg_chan_to_band(pm_conc_connection_list[2].chan); + if ((band1 == BAND_2G) || + (band2 == BAND_2G) || + (band3 == BAND_2G)) { + if (!hw_mode.dbs_cap) + return PM_DBS; + else + return PM_NOP; + } else if ((band1 == BAND_5G) && + (band2 == BAND_5G) && + (band3 == BAND_5G)) { + if (WLAN_REG_IS_CHANNEL_VALID_5G_SBS( + pm_conc_connection_list[0].chan, + pm_conc_connection_list[2].chan) && + WLAN_REG_IS_CHANNEL_VALID_5G_SBS( + pm_conc_connection_list[1].chan, + pm_conc_connection_list[2].chan) && + WLAN_REG_IS_CHANNEL_VALID_5G_SBS( + pm_conc_connection_list[0].chan, + pm_conc_connection_list[1].chan)) { + if (!hw_mode.sbs_cap) + return PM_SBS; + else + return PM_NOP; + } else { + if (hw_mode.sbs_cap || hw_mode.dbs_cap) + return PM_SINGLE_MAC; + else + return PM_NOP; + } + } else + return PM_NOP; + default: + policy_mgr_err("unexpected num_connections value %d", + num_connections); + return PM_NOP; + } +} + +/** + * policy_mgr_get_current_pref_hw_mode_dbs_1x1() - Get the + * current preferred hw mode + * + * Get the preferred hw mode based on the current connection combinations + * + * Return: No change (PM_NOP), MCC (PM_SINGLE_MAC_UPGRADE), + * DBS (PM_DBS_DOWNGRADE) + */ +enum policy_mgr_conc_next_action + policy_mgr_get_current_pref_hw_mode_dbs_1x1( + struct wlan_objmgr_psoc *psoc) +{ + uint32_t num_connections; + uint8_t band1, band2, band3; + struct policy_mgr_hw_mode_params hw_mode; + QDF_STATUS status; + enum policy_mgr_conc_next_action next_action; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return PM_NOP; + } + + status = policy_mgr_get_current_hw_mode(psoc, &hw_mode); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("policy_mgr_get_current_hw_mode failed"); + return PM_NOP; + } + + num_connections = policy_mgr_get_connection_count(psoc); + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + policy_mgr_debug("chan[0]:%d chan[1]:%d chan[2]:%d num_connections:%d dbs:%d", + pm_conc_connection_list[0].chan, + pm_conc_connection_list[1].chan, + pm_conc_connection_list[2].chan, num_connections, + hw_mode.dbs_cap); + + /* If the band of operation of both the MACs is the same, + * single MAC is preferred, otherwise DBS is preferred. + */ + switch (num_connections) { + case 1: + /* The driver would already be in the required hw mode */ + next_action = PM_NOP; + break; + case 2: + band1 = reg_chan_to_band(pm_conc_connection_list[0].chan); + band2 = reg_chan_to_band(pm_conc_connection_list[1].chan); + if ((band1 == band2) && (hw_mode.dbs_cap)) + next_action = PM_SINGLE_MAC_UPGRADE; + else if ((band1 != band2) && (!hw_mode.dbs_cap)) + next_action = PM_DBS_DOWNGRADE; + else + next_action = PM_NOP; + + break; + + case 3: + band1 = reg_chan_to_band(pm_conc_connection_list[0].chan); + band2 = reg_chan_to_band(pm_conc_connection_list[1].chan); + band3 = reg_chan_to_band(pm_conc_connection_list[2].chan); + if (((band1 == band2) && (band2 == band3)) && + (hw_mode.dbs_cap)) { + next_action = PM_SINGLE_MAC_UPGRADE; + } else if (((band1 != band2) || (band2 != band3) || + (band1 != band3)) && + (!hw_mode.dbs_cap)) { + next_action = PM_DBS_DOWNGRADE; + } else { + next_action = PM_NOP; + } + break; + default: + policy_mgr_err("unexpected num_connections value %d", + num_connections); + next_action = PM_NOP; + break; + } + + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return next_action; +} + +enum policy_mgr_conc_next_action +policy_mgr_get_current_pref_hw_mode_dual_dbs( + struct wlan_objmgr_psoc *psoc) +{ + enum policy_mgr_conc_next_action next_action; + struct policy_mgr_psoc_priv_obj *pm_ctx; + enum policy_mgr_conc_next_action preferred_dbs; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return PM_NOP; + } + + next_action = policy_mgr_get_current_pref_hw_mode_dbs_1x1(psoc); + policy_mgr_info("next_action %d", next_action); + if (next_action != PM_DBS_DOWNGRADE) + return next_action; + + preferred_dbs = policy_mgr_get_preferred_dbs_action_table( + psoc, INVALID_VDEV_ID, 0, 0); + if (preferred_dbs == PM_DBS1) { + next_action = PM_DBS1_DOWNGRADE; + } else if (preferred_dbs == PM_DBS2) { + next_action = PM_DBS2_DOWNGRADE; + } else { + policy_mgr_err("DBS1 and DBS2 hw mode not supported"); + return PM_NOP; + } + policy_mgr_info("preferred_dbs %d", next_action); + return next_action; +} + +/** + * policy_mgr_reset_sap_mandatory_channels() - Reset the SAP mandatory channels + * + * Resets the SAP mandatory channel list and the length of the list + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_reset_sap_mandatory_channels( + struct policy_mgr_psoc_priv_obj *pm_ctx) +{ + pm_ctx->sap_mandatory_channels_len = 0; + qdf_mem_zero(pm_ctx->sap_mandatory_channels, + QDF_ARRAY_SIZE(pm_ctx->sap_mandatory_channels)); + + return QDF_STATUS_SUCCESS; +} + +void policy_mgr_enable_disable_sap_mandatory_chan_list( + struct wlan_objmgr_psoc *psoc, bool val) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + pm_ctx->enable_sap_mandatory_chan_list = val; +} + +void policy_mgr_add_sap_mandatory_chan(struct wlan_objmgr_psoc *psoc, + uint8_t chan) +{ + int i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + for (i = 0; i < pm_ctx->sap_mandatory_channels_len; i++) { + if (chan == pm_ctx->sap_mandatory_channels[i]) + return; + } + + policy_mgr_debug("chan %hu", chan); + pm_ctx->sap_mandatory_channels[pm_ctx->sap_mandatory_channels_len++] + = chan; +} + +bool policy_mgr_is_sap_mandatory_chan_list_enabled( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + return pm_ctx->enable_sap_mandatory_chan_list; +} + +uint32_t policy_mgr_get_sap_mandatory_chan_list_len( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return 0; + } + + return pm_ctx->sap_mandatory_channels_len; +} + +void policy_mgr_init_sap_mandatory_2g_chan(struct wlan_objmgr_psoc *psoc) +{ + uint8_t chan_list[QDF_MAX_NUM_CHAN] = {0}; + uint32_t len = 0; + int i; + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + status = policy_mgr_get_valid_chans(psoc, chan_list, &len); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Error in getting valid channels"); + return; + } + pm_ctx->sap_mandatory_channels_len = 0; + + for (i = 0; (i < len) && (i < QDF_MAX_NUM_CHAN); i++) { + if (WLAN_REG_IS_24GHZ_CH(chan_list[i])) { + policy_mgr_debug("Add chan %hu to mandatory list", + chan_list[i]); + pm_ctx->sap_mandatory_channels[ + pm_ctx->sap_mandatory_channels_len++] = + chan_list[i]; + } + } +} + +void policy_mgr_remove_sap_mandatory_chan(struct wlan_objmgr_psoc *psoc, + uint8_t chan) +{ + uint8_t chan_list[QDF_MAX_NUM_CHAN] = {0}; + uint32_t num_chan = 0; + int i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + if (pm_ctx->sap_mandatory_channels_len >= QDF_MAX_NUM_CHAN) { + policy_mgr_err("Invalid channel len %d ", + pm_ctx->sap_mandatory_channels_len); + return; + } + + for (i = 0; i < pm_ctx->sap_mandatory_channels_len; i++) { + if (chan == pm_ctx->sap_mandatory_channels[i]) + continue; + chan_list[num_chan++] = pm_ctx->sap_mandatory_channels[i]; + } + + qdf_mem_zero(pm_ctx->sap_mandatory_channels, + pm_ctx->sap_mandatory_channels_len); + qdf_mem_copy(pm_ctx->sap_mandatory_channels, chan_list, num_chan); + pm_ctx->sap_mandatory_channels_len = num_chan; +} diff --git a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_get_set_utils.c b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_get_set_utils.c new file mode 100644 index 000000000000..c41b794d0a6b --- /dev/null +++ b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_get_set_utils.c @@ -0,0 +1,3463 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_policy_mgr_get_set_utils.c + * + * WLAN Concurrenct Connection Management APIs + * + */ + +/* Include files */ +#include "target_if.h" +#include "wlan_policy_mgr_api.h" +#include "wlan_policy_mgr_i.h" +#include "qdf_types.h" +#include "qdf_trace.h" +#include "wlan_objmgr_global_obj.h" +#include "wlan_objmgr_pdev_obj.h" +#include "wlan_objmgr_vdev_obj.h" + +/* invalid channel id. */ +#define INVALID_CHANNEL_ID 0 + +QDF_STATUS policy_mgr_get_mcc_scc_switch(struct wlan_objmgr_psoc *psoc, + uint8_t *mcc_scc_switch) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *mcc_scc_switch = pm_ctx->cfg.mcc_to_scc_switch; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_sys_pref(struct wlan_objmgr_psoc *psoc, + uint8_t *sys_pref) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *sys_pref = pm_ctx->cfg.sys_pref; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_set_sys_pref(struct wlan_objmgr_psoc *psoc, + uint8_t sys_pref) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + pm_ctx->cfg.sys_pref = sys_pref; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_max_conc_cxns(struct wlan_objmgr_psoc *psoc, + uint8_t *max_conc_cxns) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *max_conc_cxns = pm_ctx->cfg.max_conc_cxns; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_conc_rule1(struct wlan_objmgr_psoc *psoc, + uint8_t *conc_rule1) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *conc_rule1 = pm_ctx->cfg.conc_rule1; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_conc_rule2(struct wlan_objmgr_psoc *psoc, + uint8_t *conc_rule2) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *conc_rule2 = pm_ctx->cfg.conc_rule2; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_dbs_selection_plcy(struct wlan_objmgr_psoc *psoc, + uint32_t *dbs_selection_plcy) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *dbs_selection_plcy = pm_ctx->cfg.dbs_selection_plcy; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_vdev_priority_list(struct wlan_objmgr_psoc *psoc, + uint32_t *vdev_priority_list) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *vdev_priority_list = pm_ctx->cfg.vdev_priority_list; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_chnl_select_plcy(struct wlan_objmgr_psoc *psoc, + uint32_t *chnl_select_plcy) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *chnl_select_plcy = pm_ctx->cfg.chnl_select_plcy; + + return QDF_STATUS_SUCCESS; +} + + +QDF_STATUS policy_mgr_get_mcc_adaptive_sch(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_mcc_adaptive_sch) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *enable_mcc_adaptive_sch = pm_ctx->cfg.enable_mcc_adaptive_sch; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_sta_cxn_5g_band(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_sta_cxn_5g_band) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + *enable_sta_cxn_5g_band = pm_ctx->cfg.enable_sta_cxn_5g_band; + + return QDF_STATUS_SUCCESS; +} + +void policy_mgr_update_new_hw_mode_index(struct wlan_objmgr_psoc *psoc, + uint32_t new_hw_mode_index) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + pm_ctx->new_hw_mode_index = new_hw_mode_index; +} + +void policy_mgr_update_old_hw_mode_index(struct wlan_objmgr_psoc *psoc, + uint32_t old_hw_mode_index) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + pm_ctx->old_hw_mode_index = old_hw_mode_index; +} + +void policy_mgr_update_hw_mode_index(struct wlan_objmgr_psoc *psoc, + uint32_t new_hw_mode_index) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + if (POLICY_MGR_DEFAULT_HW_MODE_INDEX == pm_ctx->new_hw_mode_index) { + pm_ctx->new_hw_mode_index = new_hw_mode_index; + } else { + pm_ctx->old_hw_mode_index = pm_ctx->new_hw_mode_index; + pm_ctx->new_hw_mode_index = new_hw_mode_index; + } + policy_mgr_debug("Updated: old_hw_mode_index:%d new_hw_mode_index:%d", + pm_ctx->old_hw_mode_index, pm_ctx->new_hw_mode_index); +} + +/** + * policy_mgr_get_num_of_setbits_from_bitmask() - to get num of + * setbits from bitmask + * @mask: given bitmask + * + * This helper function should return number of setbits from bitmask + * + * Return: number of setbits from bitmask + */ +static uint32_t policy_mgr_get_num_of_setbits_from_bitmask(uint32_t mask) +{ + uint32_t num_of_setbits = 0; + + while (mask) { + mask &= (mask - 1); + num_of_setbits++; + } + return num_of_setbits; +} + +/** + * policy_mgr_map_wmi_channel_width_to_hw_mode_bw() - returns + * bandwidth in terms of hw_mode_bandwidth + * @width: bandwidth in terms of wmi_channel_width + * + * This function returns the bandwidth in terms of hw_mode_bandwidth. + * + * Return: BW in terms of hw_mode_bandwidth. + */ +static enum hw_mode_bandwidth policy_mgr_map_wmi_channel_width_to_hw_mode_bw( + wmi_channel_width width) +{ + switch (width) { + case WMI_CHAN_WIDTH_20: + return HW_MODE_20_MHZ; + case WMI_CHAN_WIDTH_40: + return HW_MODE_40_MHZ; + case WMI_CHAN_WIDTH_80: + return HW_MODE_80_MHZ; + case WMI_CHAN_WIDTH_160: + return HW_MODE_160_MHZ; + case WMI_CHAN_WIDTH_80P80: + return HW_MODE_80_PLUS_80_MHZ; + case WMI_CHAN_WIDTH_5: + return HW_MODE_5_MHZ; + case WMI_CHAN_WIDTH_10: + return HW_MODE_10_MHZ; + default: + return HW_MODE_BW_NONE; + } + + return HW_MODE_BW_NONE; +} + +static void policy_mgr_get_hw_mode_params( + struct wlan_psoc_host_mac_phy_caps *caps, + struct policy_mgr_mac_ss_bw_info *info) +{ + if (!caps) { + policy_mgr_err("Invalid capabilities"); + return; + } + + info->mac_tx_stream = policy_mgr_get_num_of_setbits_from_bitmask( + QDF_MAX(caps->tx_chain_mask_2G, + caps->tx_chain_mask_5G)); + info->mac_rx_stream = policy_mgr_get_num_of_setbits_from_bitmask( + QDF_MAX(caps->rx_chain_mask_2G, + caps->rx_chain_mask_5G)); + info->mac_bw = policy_mgr_map_wmi_channel_width_to_hw_mode_bw( + QDF_MAX(caps->max_bw_supported_2G, + caps->max_bw_supported_5G)); + info->mac_band_cap = caps->supported_bands; +} + +/** + * policy_mgr_set_hw_mode_params() - sets TX-RX stream, + * bandwidth and DBS in hw_mode_list + * @wma_handle: pointer to wma global structure + * @mac0_ss_bw_info: TX-RX streams, BW for MAC0 + * @mac1_ss_bw_info: TX-RX streams, BW for MAC1 + * @pos: refers to hw_mode_list array index + * @hw_mode_id: hw mode id value used by firmware + * @dbs_mode: dbs_mode for the dbs_hw_mode + * @sbs_mode: sbs_mode for the sbs_hw_mode + * + * This function sets TX-RX stream, bandwidth and DBS mode in + * hw_mode_list. + * + * Return: none + */ +static void policy_mgr_set_hw_mode_params(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_mac_ss_bw_info mac0_ss_bw_info, + struct policy_mgr_mac_ss_bw_info mac1_ss_bw_info, + uint32_t pos, uint32_t hw_mode_id, uint32_t dbs_mode, + uint32_t sbs_mode) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + mac0_ss_bw_info.mac_tx_stream); + POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + mac0_ss_bw_info.mac_rx_stream); + POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + mac0_ss_bw_info.mac_bw); + POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + mac1_ss_bw_info.mac_tx_stream); + POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + mac1_ss_bw_info.mac_rx_stream); + POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + mac1_ss_bw_info.mac_bw); + POLICY_MGR_HW_MODE_DBS_MODE_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + dbs_mode); + POLICY_MGR_HW_MODE_AGILE_DFS_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + HW_MODE_AGILE_DFS_NONE); + POLICY_MGR_HW_MODE_SBS_MODE_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + sbs_mode); + POLICY_MGR_HW_MODE_MAC0_BAND_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + mac0_ss_bw_info.mac_band_cap); + POLICY_MGR_HW_MODE_ID_SET( + pm_ctx->hw_mode.hw_mode_list[pos], + hw_mode_id); +} + +QDF_STATUS policy_mgr_update_hw_mode_list(struct wlan_objmgr_psoc *psoc, + struct target_psoc_info *tgt_hdl) +{ + struct wlan_psoc_host_mac_phy_caps *tmp; + uint32_t i, hw_config_type, j = 0; + uint32_t dbs_mode, sbs_mode; + struct policy_mgr_mac_ss_bw_info mac0_ss_bw_info = {0}; + struct policy_mgr_mac_ss_bw_info mac1_ss_bw_info = {0}; + struct policy_mgr_psoc_priv_obj *pm_ctx; + struct tgt_info *info; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + info = &tgt_hdl->info; + if (!info->service_ext_param.num_hw_modes) { + policy_mgr_err("Number of HW modes: %d", + info->service_ext_param.num_hw_modes); + return QDF_STATUS_E_FAILURE; + } + + /* + * This list was updated as part of service ready event. Re-populate + * HW mode list from the device capabilities. + */ + if (pm_ctx->hw_mode.hw_mode_list) { + qdf_mem_free(pm_ctx->hw_mode.hw_mode_list); + pm_ctx->hw_mode.hw_mode_list = NULL; + policy_mgr_debug("DBS list is freed"); + } + + pm_ctx->num_dbs_hw_modes = info->service_ext_param.num_hw_modes; + pm_ctx->hw_mode.hw_mode_list = + qdf_mem_malloc(sizeof(*pm_ctx->hw_mode.hw_mode_list) * + pm_ctx->num_dbs_hw_modes); + if (!pm_ctx->hw_mode.hw_mode_list) { + policy_mgr_err("Memory allocation failed for DBS"); + return QDF_STATUS_E_FAILURE; + } + + policy_mgr_debug("Updated HW mode list: Num modes:%d", + pm_ctx->num_dbs_hw_modes); + + for (i = 0; i < pm_ctx->num_dbs_hw_modes; i++) { + /* Update for MAC0 */ + tmp = &info->mac_phy_cap[j++]; + policy_mgr_get_hw_mode_params(tmp, &mac0_ss_bw_info); + hw_config_type = tmp->hw_mode_config_type; + dbs_mode = HW_MODE_DBS_NONE; + sbs_mode = HW_MODE_SBS_NONE; + mac1_ss_bw_info.mac_tx_stream = 0; + mac1_ss_bw_info.mac_rx_stream = 0; + mac1_ss_bw_info.mac_bw = 0; + + /* SBS and DBS have dual MAC. Upto 2 MACs are considered. */ + if ((hw_config_type == WMI_HW_MODE_DBS) || + (hw_config_type == WMI_HW_MODE_SBS_PASSIVE) || + (hw_config_type == WMI_HW_MODE_SBS)) { + /* Update for MAC1 */ + tmp = &info->mac_phy_cap[j++]; + policy_mgr_get_hw_mode_params(tmp, &mac1_ss_bw_info); + if (hw_config_type == WMI_HW_MODE_DBS) + dbs_mode = HW_MODE_DBS; + if ((hw_config_type == WMI_HW_MODE_SBS_PASSIVE) || + (hw_config_type == WMI_HW_MODE_SBS)) + sbs_mode = HW_MODE_SBS; + } + + /* Updating HW mode list */ + policy_mgr_set_hw_mode_params(psoc, mac0_ss_bw_info, + mac1_ss_bw_info, i, tmp->hw_mode_id, dbs_mode, + sbs_mode); + } + return QDF_STATUS_SUCCESS; +} + +void policy_mgr_init_dbs_hw_mode(struct wlan_objmgr_psoc *psoc, + uint32_t num_dbs_hw_modes, + uint32_t *ev_wlan_dbs_hw_mode_list) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + pm_ctx->num_dbs_hw_modes = num_dbs_hw_modes; + pm_ctx->hw_mode.hw_mode_list = + qdf_mem_malloc(sizeof(*pm_ctx->hw_mode.hw_mode_list) * + pm_ctx->num_dbs_hw_modes); + if (!pm_ctx->hw_mode.hw_mode_list) { + policy_mgr_err("Memory allocation failed for DBS"); + return; + } + qdf_mem_copy(pm_ctx->hw_mode.hw_mode_list, + ev_wlan_dbs_hw_mode_list, + (sizeof(*pm_ctx->hw_mode.hw_mode_list) * + pm_ctx->num_dbs_hw_modes)); +} + +void policy_mgr_dump_dbs_hw_mode(struct wlan_objmgr_psoc *psoc) +{ + uint32_t i, param; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + for (i = 0; i < pm_ctx->num_dbs_hw_modes; i++) { + param = pm_ctx->hw_mode.hw_mode_list[i]; + policy_mgr_debug("[%d]-MAC0: tx_ss:%d rx_ss:%d bw_idx:%d band_cap:%d", + i, + POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_GET(param), + POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_GET(param), + POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_GET(param), + POLICY_MGR_HW_MODE_MAC0_BAND_GET(param)); + policy_mgr_debug("[%d]-MAC1: tx_ss:%d rx_ss:%d bw_idx:%d", + i, + POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_GET(param), + POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_GET(param), + POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_GET(param)); + policy_mgr_debug("[%d] DBS:%d SBS:%d hw_mode_id:%d", i, + POLICY_MGR_HW_MODE_DBS_MODE_GET(param), + POLICY_MGR_HW_MODE_SBS_MODE_GET(param), + POLICY_MGR_HW_MODE_ID_GET(param)); + } +} + +void policy_mgr_init_dbs_config(struct wlan_objmgr_psoc *psoc, + uint32_t scan_config, uint32_t fw_config) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + pm_ctx->dual_mac_cfg.cur_scan_config = 0; + pm_ctx->dual_mac_cfg.cur_fw_mode_config = 0; + + /* If dual mac features are disabled in the INI, we + * need not proceed further + */ + if (DISABLE_DBS_CXN_AND_SCAN == + wlan_objmgr_psoc_get_dual_mac_disable(psoc)) { + policy_mgr_err("Disabling dual mac capabilities"); + /* All capabilities are initialized to 0. We can return */ + goto done; + } + + /* Initialize concurrent_scan_config_bits with default FW value */ + WMI_DBS_CONC_SCAN_CFG_ASYNC_DBS_SCAN_SET( + pm_ctx->dual_mac_cfg.cur_scan_config, + WMI_DBS_CONC_SCAN_CFG_ASYNC_DBS_SCAN_GET(scan_config)); + WMI_DBS_CONC_SCAN_CFG_SYNC_DBS_SCAN_SET( + pm_ctx->dual_mac_cfg.cur_scan_config, + WMI_DBS_CONC_SCAN_CFG_SYNC_DBS_SCAN_GET(scan_config)); + WMI_DBS_CONC_SCAN_CFG_DBS_SCAN_SET( + pm_ctx->dual_mac_cfg.cur_scan_config, + WMI_DBS_CONC_SCAN_CFG_DBS_SCAN_GET(scan_config)); + WMI_DBS_CONC_SCAN_CFG_AGILE_SCAN_SET( + pm_ctx->dual_mac_cfg.cur_scan_config, + WMI_DBS_CONC_SCAN_CFG_AGILE_SCAN_GET(scan_config)); + WMI_DBS_CONC_SCAN_CFG_AGILE_DFS_SCAN_SET( + pm_ctx->dual_mac_cfg.cur_scan_config, + WMI_DBS_CONC_SCAN_CFG_AGILE_DFS_SCAN_GET(scan_config)); + + /* Initialize fw_mode_config_bits with default FW value */ + WMI_DBS_FW_MODE_CFG_DBS_SET( + pm_ctx->dual_mac_cfg.cur_fw_mode_config, + WMI_DBS_FW_MODE_CFG_DBS_GET(fw_config)); + WMI_DBS_FW_MODE_CFG_AGILE_DFS_SET( + pm_ctx->dual_mac_cfg.cur_fw_mode_config, + WMI_DBS_FW_MODE_CFG_AGILE_DFS_GET(fw_config)); + WMI_DBS_FW_MODE_CFG_DBS_FOR_CXN_SET( + pm_ctx->dual_mac_cfg.cur_fw_mode_config, + WMI_DBS_FW_MODE_CFG_DBS_FOR_CXN_GET(fw_config)); +done: + /* Initialize the previous scan/fw mode config */ + pm_ctx->dual_mac_cfg.prev_scan_config = + pm_ctx->dual_mac_cfg.cur_scan_config; + pm_ctx->dual_mac_cfg.prev_fw_mode_config = + pm_ctx->dual_mac_cfg.cur_fw_mode_config; + + policy_mgr_debug("cur_scan_config:%x cur_fw_mode_config:%x", + pm_ctx->dual_mac_cfg.cur_scan_config, + pm_ctx->dual_mac_cfg.cur_fw_mode_config); +} + +void policy_mgr_update_dbs_scan_config(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + pm_ctx->dual_mac_cfg.prev_scan_config = + pm_ctx->dual_mac_cfg.cur_scan_config; + pm_ctx->dual_mac_cfg.cur_scan_config = + pm_ctx->dual_mac_cfg.req_scan_config; +} + +void policy_mgr_update_dbs_fw_config(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + pm_ctx->dual_mac_cfg.prev_fw_mode_config = + pm_ctx->dual_mac_cfg.cur_fw_mode_config; + pm_ctx->dual_mac_cfg.cur_fw_mode_config = + pm_ctx->dual_mac_cfg.req_fw_mode_config; +} + +void policy_mgr_update_dbs_req_config(struct wlan_objmgr_psoc *psoc, + uint32_t scan_config, uint32_t fw_mode_config) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + pm_ctx->dual_mac_cfg.req_scan_config = scan_config; + pm_ctx->dual_mac_cfg.req_fw_mode_config = fw_mode_config; +} + +bool policy_mgr_get_dbs_plus_agile_scan_config(struct wlan_objmgr_psoc *psoc) +{ + uint32_t scan_config; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + if (policy_mgr_is_dual_mac_disabled_in_ini(psoc)) + return false; + + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + /* We take that it is disabled and proceed */ + return false; + } + scan_config = pm_ctx->dual_mac_cfg.cur_scan_config; + + return WMI_DBS_CONC_SCAN_CFG_AGILE_SCAN_GET(scan_config); +} + +bool policy_mgr_get_single_mac_scan_with_dfs_config( + struct wlan_objmgr_psoc *psoc) +{ + uint32_t scan_config; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + if (policy_mgr_is_dual_mac_disabled_in_ini(psoc)) + return false; + + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + /* We take that it is disabled and proceed */ + return false; + } + scan_config = pm_ctx->dual_mac_cfg.cur_scan_config; + + return WMI_DBS_CONC_SCAN_CFG_AGILE_DFS_SCAN_GET(scan_config); +} + +int8_t policy_mgr_get_num_dbs_hw_modes(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return -EINVAL; + } + return pm_ctx->num_dbs_hw_modes; +} + +bool policy_mgr_is_hw_dbs_capable(struct wlan_objmgr_psoc *psoc) +{ + uint32_t param, i, found = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + struct wmi_unified *wmi_handle; + + pm_ctx = policy_mgr_get_context(psoc); + + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + if (!policy_mgr_is_dbs_enable(psoc)) { + policy_mgr_debug("DBS is disabled"); + return false; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + policy_mgr_debug("Invalid WMI handle"); + return false; + } + + policy_mgr_debug("DBS service bit map: %d", + wmi_service_enabled(wmi_handle, + wmi_service_dual_band_simultaneous_support)); + + /* The agreement with FW is that: To know if the target is DBS + * capable, DBS needs to be supported both in the HW mode list + * and in the service ready event + */ + if (!(wmi_service_enabled(wmi_handle, + wmi_service_dual_band_simultaneous_support))) + return false; + + for (i = 0; i < pm_ctx->num_dbs_hw_modes; i++) { + param = pm_ctx->hw_mode.hw_mode_list[i]; + policy_mgr_debug("HW param: %x", param); + if (POLICY_MGR_HW_MODE_DBS_MODE_GET(param)) { + policy_mgr_debug("HW (%d) is DBS capable", i); + found = 1; + break; + } + } + + if (found) + return true; + + return false; +} + +bool policy_mgr_is_hw_sbs_capable(struct wlan_objmgr_psoc *psoc) +{ + uint32_t param, i, found = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + struct wmi_unified *wmi_handle; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + policy_mgr_debug("Invalid WMI handle"); + return false; + } + + policy_mgr_debug("DBS service bit map: %d", + wmi_service_enabled(wmi_handle, + wmi_service_dual_band_simultaneous_support)); + + /* The agreement with FW is that: To know if the target is SBS + * capable, SBS needs to be supported both in the HW mode list + * and DBS needs to be supported in the service ready event + */ + if (!(wmi_service_enabled(wmi_handle, + wmi_service_dual_band_simultaneous_support))) + return false; + + for (i = 0; i < pm_ctx->num_dbs_hw_modes; i++) { + param = pm_ctx->hw_mode.hw_mode_list[i]; + policy_mgr_debug("HW param: %x", param); + if (POLICY_MGR_HW_MODE_SBS_MODE_GET(param)) { + policy_mgr_debug("HW (%d) is SBS capable", i); + found = 1; + break; + } + } + + if (found) + return true; + + return false; +} + +QDF_STATUS policy_mgr_get_dbs_hw_modes(struct wlan_objmgr_psoc *psoc, + bool *one_by_one_dbs, bool *two_by_two_dbs) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t i; + int8_t found_one_by_one = -EINVAL, found_two_by_two = -EINVAL; + uint32_t conf1_tx_ss, conf1_rx_ss; + uint32_t conf2_tx_ss, conf2_rx_ss; + + *one_by_one_dbs = false; + *two_by_two_dbs = false; + + if (policy_mgr_is_hw_dbs_capable(psoc) == false) { + policy_mgr_err("HW is not DBS capable"); + /* Caller will understand that DBS is disabled */ + return QDF_STATUS_SUCCESS; + + } + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + /* To check 1x1 capability */ + policy_mgr_get_tx_rx_ss_from_config(HW_MODE_SS_1x1, + &conf1_tx_ss, &conf1_rx_ss); + /* To check 2x2 capability */ + policy_mgr_get_tx_rx_ss_from_config(HW_MODE_SS_2x2, + &conf2_tx_ss, &conf2_rx_ss); + + for (i = 0; i < pm_ctx->num_dbs_hw_modes; i++) { + uint32_t t_conf0_tx_ss, t_conf0_rx_ss; + uint32_t t_conf1_tx_ss, t_conf1_rx_ss; + uint32_t dbs_mode; + + t_conf0_tx_ss = POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + t_conf0_rx_ss = POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + t_conf1_tx_ss = POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + t_conf1_rx_ss = POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + dbs_mode = POLICY_MGR_HW_MODE_DBS_MODE_GET( + pm_ctx->hw_mode.hw_mode_list[i]); + + if (((((t_conf0_tx_ss == conf1_tx_ss) && + (t_conf0_rx_ss == conf1_rx_ss)) || + ((t_conf1_tx_ss == conf1_tx_ss) && + (t_conf1_rx_ss == conf1_rx_ss))) && + (dbs_mode == HW_MODE_DBS)) && + (found_one_by_one < 0)) { + found_one_by_one = i; + policy_mgr_debug("1x1 hw_mode index %d found", i); + /* Once an entry is found, need not check for 1x1 + * again + */ + continue; + } + + if (((((t_conf0_tx_ss == conf2_tx_ss) && + (t_conf0_rx_ss == conf2_rx_ss)) || + ((t_conf1_tx_ss == conf2_tx_ss) && + (t_conf1_rx_ss == conf2_rx_ss))) && + (dbs_mode == HW_MODE_DBS)) && + (found_two_by_two < 0)) { + found_two_by_two = i; + policy_mgr_debug("2x2 hw_mode index %d found", i); + /* Once an entry is found, need not check for 2x2 + * again + */ + continue; + } + } + + if (found_one_by_one >= 0) + *one_by_one_dbs = true; + if (found_two_by_two >= 0) + *two_by_two_dbs = true; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_current_hw_mode(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_hw_mode_params *hw_mode) +{ + QDF_STATUS status; + uint32_t old_hw_index = 0, new_hw_index = 0; + + policy_mgr_debug("Get the current hw mode"); + + status = policy_mgr_get_old_and_new_hw_index(psoc, &old_hw_index, + &new_hw_index); + if (QDF_STATUS_SUCCESS != status) { + policy_mgr_err("Failed to get HW mode index"); + return QDF_STATUS_E_FAILURE; + } + + if (new_hw_index == POLICY_MGR_DEFAULT_HW_MODE_INDEX) { + policy_mgr_err("HW mode is not yet initialized"); + return QDF_STATUS_E_FAILURE; + } + + status = policy_mgr_get_hw_mode_from_idx(psoc, new_hw_index, hw_mode); + if (QDF_STATUS_SUCCESS != status) { + policy_mgr_err("Failed to get HW mode index"); + return QDF_STATUS_E_FAILURE; + } + return QDF_STATUS_SUCCESS; +} + +bool policy_mgr_is_current_hwmode_dbs(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_hw_mode_params hw_mode; + + if (!policy_mgr_is_hw_dbs_capable(psoc)) + return false; + if (QDF_STATUS_SUCCESS != + policy_mgr_get_current_hw_mode(psoc, &hw_mode)) + return false; + if (hw_mode.dbs_cap) + return true; + return false; +} + +bool policy_mgr_is_dbs_enable(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + if (policy_mgr_is_dual_mac_disabled_in_ini(psoc)) { + policy_mgr_debug("DBS is disabled from ini"); + return false; + } + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + policy_mgr_debug("DBS=%d", + WMI_DBS_FW_MODE_CFG_DBS_GET( + pm_ctx->dual_mac_cfg.cur_fw_mode_config)); + + if (WMI_DBS_FW_MODE_CFG_DBS_GET( + pm_ctx->dual_mac_cfg.cur_fw_mode_config)) + return true; + + return false; +} + +bool policy_mgr_is_hw_dbs_2x2_capable(struct wlan_objmgr_psoc *psoc) +{ + struct dbs_nss nss_dbs; + uint32_t nss; + + nss = policy_mgr_get_hw_dbs_nss(psoc, &nss_dbs); + if (nss >= HW_MODE_SS_2x2 && (nss_dbs.mac0_ss == nss_dbs.mac1_ss)) + return true; + else + return false; +} + +/* + * policy_mgr_is_2x2_1x1_dbs_capable() - check 2x2+1x1 DBS supported or not + * @psoc: PSOC object data + * + * This routine is called to check 2x2 5G + 1x1 2G (DBS1) or + * 2x2 2G + 1x1 5G (DBS2) support or not. + * Either DBS1 or DBS2 supported + * + * Return: true/false + */ +bool policy_mgr_is_2x2_1x1_dbs_capable(struct wlan_objmgr_psoc *psoc) +{ + struct dbs_nss nss_dbs; + uint32_t nss; + + nss = policy_mgr_get_hw_dbs_nss(psoc, &nss_dbs); + if (nss >= HW_MODE_SS_2x2 && (nss_dbs.mac0_ss > nss_dbs.mac1_ss)) + return true; + else + return false; +} + +/* + * policy_mgr_is_2x2_5G_1x1_2G_dbs_capable() - check Genoa DBS1 enabled or not + * @psoc: PSOC object data + * + * This routine is called to check support DBS1 or not. + * Notes: DBS1: 2x2 5G + 1x1 2G. + * This function will call policy_mgr_get_hw_mode_idx_from_dbs_hw_list to match + * the HW mode from hw mode list. The parameters will also be matched to + * 2x2 5G +2x2 2G HW mode. But firmware will not report 2x2 5G + 2x2 2G alone + * with 2x2 5G + 1x1 2G at same time. So, it is safe to find DBS1 with + * policy_mgr_get_hw_mode_idx_from_dbs_hw_list. + * + * Return: true/false + */ +bool policy_mgr_is_2x2_5G_1x1_2G_dbs_capable(struct wlan_objmgr_psoc *psoc) +{ + return policy_mgr_is_2x2_1x1_dbs_capable(psoc) && + (policy_mgr_get_hw_mode_idx_from_dbs_hw_list( + psoc, + HW_MODE_SS_2x2, + HW_MODE_80_MHZ, + HW_MODE_SS_1x1, HW_MODE_40_MHZ, + HW_MODE_MAC_BAND_5G, + HW_MODE_DBS, + HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS_NONE) >= 0); +} + +/* + * policy_mgr_is_2x2_2G_1x1_5G_dbs_capable() - check Genoa DBS2 enabled or not + * @psoc: PSOC object data + * + * This routine is called to check support DBS2 or not. + * Notes: DBS2: 2x2 2G + 1x1 5G + * + * Return: true/false + */ +bool policy_mgr_is_2x2_2G_1x1_5G_dbs_capable(struct wlan_objmgr_psoc *psoc) +{ + return policy_mgr_is_2x2_1x1_dbs_capable(psoc) && + (policy_mgr_get_hw_mode_idx_from_dbs_hw_list( + psoc, + HW_MODE_SS_2x2, + HW_MODE_40_MHZ, + HW_MODE_SS_1x1, HW_MODE_40_MHZ, + HW_MODE_MAC_BAND_2G, + HW_MODE_DBS, + HW_MODE_AGILE_DFS_NONE, + HW_MODE_SBS_NONE) >= 0); +} + +uint32_t policy_mgr_get_connection_count(struct wlan_objmgr_psoc *psoc) +{ + uint32_t conn_index, count = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return count; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if (pm_conc_connection_list[conn_index].in_use) + count++; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return count; +} + +uint32_t policy_mgr_mode_specific_vdev_id(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode) +{ + uint32_t conn_index = 0; + uint32_t vdev_id = WLAN_INVALID_VDEV_ID; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return vdev_id; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + /* + * Note: This gives you the first vdev id of the mode type in a + * sta+sta or sap+sap or p2p + p2p case + */ + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if ((pm_conc_connection_list[conn_index].mode == mode) && + pm_conc_connection_list[conn_index].in_use) { + vdev_id = pm_conc_connection_list[conn_index].vdev_id; + break; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return vdev_id; +} + +uint32_t policy_mgr_mode_specific_connection_count( + struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint32_t *list) +{ + uint32_t conn_index = 0, count = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return count; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if ((pm_conc_connection_list[conn_index].mode == mode) && + pm_conc_connection_list[conn_index].in_use) { + if (list != NULL) + list[count] = conn_index; + count++; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return count; +} + +QDF_STATUS policy_mgr_check_conn_with_mode_and_vdev_id( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode, + uint32_t vdev_id) +{ + QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; + uint32_t conn_index = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return qdf_status; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if ((pm_conc_connection_list[conn_index].mode == mode) && + (pm_conc_connection_list[conn_index].vdev_id == vdev_id)) { + qdf_status = QDF_STATUS_SUCCESS; + break; + } + conn_index++; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + return qdf_status; +} + +void policy_mgr_soc_set_dual_mac_cfg_cb(enum set_hw_mode_status status, + uint32_t scan_config, + uint32_t fw_mode_config) +{ + policy_mgr_debug("Status:%d for scan_config:%x fw_mode_config:%x", + status, scan_config, fw_mode_config); +} + +void policy_mgr_set_dual_mac_scan_config(struct wlan_objmgr_psoc *psoc, + uint8_t dbs_val, + uint8_t dbs_plus_agile_scan_val, + uint8_t single_mac_scan_with_dbs_val) +{ + struct policy_mgr_dual_mac_config cfg; + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + /* Any non-zero positive value is treated as 1 */ + if (dbs_val != 0) + dbs_val = 1; + if (dbs_plus_agile_scan_val != 0) + dbs_plus_agile_scan_val = 1; + if (single_mac_scan_with_dbs_val != 0) + single_mac_scan_with_dbs_val = 1; + + status = policy_mgr_get_updated_scan_config(psoc, &cfg.scan_config, + dbs_val, + dbs_plus_agile_scan_val, + single_mac_scan_with_dbs_val); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("policy_mgr_get_updated_scan_config failed %d", + status); + return; + } + + status = policy_mgr_get_updated_fw_mode_config(psoc, + &cfg.fw_mode_config, + policy_mgr_get_dbs_config(psoc), + policy_mgr_get_agile_dfs_config(psoc)); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("policy_mgr_get_updated_fw_mode_config failed %d", + status); + return; + } + + cfg.set_dual_mac_cb = policy_mgr_soc_set_dual_mac_cfg_cb; + + policy_mgr_debug("scan_config:%x fw_mode_config:%x", + cfg.scan_config, cfg.fw_mode_config); + + status = pm_ctx->sme_cbacks.sme_soc_set_dual_mac_config(cfg); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("sme_soc_set_dual_mac_config failed %d", status); +} + +void policy_mgr_set_dual_mac_fw_mode_config(struct wlan_objmgr_psoc *psoc, + uint8_t dbs, uint8_t dfs) +{ + struct policy_mgr_dual_mac_config cfg; + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + /* Any non-zero positive value is treated as 1 */ + if (dbs != 0) + dbs = 1; + if (dfs != 0) + dfs = 1; + + status = policy_mgr_get_updated_scan_config(psoc, &cfg.scan_config, + policy_mgr_get_dbs_scan_config(psoc), + policy_mgr_get_dbs_plus_agile_scan_config(psoc), + policy_mgr_get_single_mac_scan_with_dfs_config(psoc)); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("policy_mgr_get_updated_scan_config failed %d", + status); + return; + } + + status = policy_mgr_get_updated_fw_mode_config(psoc, + &cfg.fw_mode_config, dbs, dfs); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("policy_mgr_get_updated_fw_mode_config failed %d", + status); + return; + } + + cfg.set_dual_mac_cb = policy_mgr_soc_set_dual_mac_cfg_cb; + + policy_mgr_debug("scan_config:%x fw_mode_config:%x", + cfg.scan_config, cfg.fw_mode_config); + + status = pm_ctx->sme_cbacks.sme_soc_set_dual_mac_config(cfg); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("sme_soc_set_dual_mac_config failed %d", status); +} + +bool policy_mgr_current_concurrency_is_mcc(struct wlan_objmgr_psoc *psoc) +{ + uint32_t num_connections = 0; + bool is_mcc = false; + + num_connections = policy_mgr_get_connection_count(psoc); + + switch (num_connections) { + case 1: + break; + case 2: + if ((pm_conc_connection_list[0].chan != + pm_conc_connection_list[1].chan) && + (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac)) { + is_mcc = true; + } + break; + case 3: + if ((pm_conc_connection_list[0].chan != + pm_conc_connection_list[1].chan) || + (pm_conc_connection_list[0].chan != + pm_conc_connection_list[2].chan) || + (pm_conc_connection_list[1].chan != + pm_conc_connection_list[2].chan)){ + is_mcc = true; + } + break; + default: + policy_mgr_err("unexpected num_connections value %d", + num_connections); + break; + } + + return is_mcc; +} + +/** + * policy_mgr_set_concurrency_mode() - To set concurrency mode + * @psoc: PSOC object data + * @mode: device mode + * + * This routine is called to set the concurrency mode + * + * Return: NONE + */ +void policy_mgr_set_concurrency_mode(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return; + } + + switch (mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_P2P_GO_MODE: + case QDF_SAP_MODE: + case QDF_IBSS_MODE: + case QDF_MONITOR_MODE: + pm_ctx->concurrency_mode |= (1 << mode); + pm_ctx->no_of_open_sessions[mode]++; + break; + default: + break; + } + + policy_mgr_info("concurrency_mode = 0x%x Number of open sessions for mode %d = %d", + pm_ctx->concurrency_mode, mode, + pm_ctx->no_of_open_sessions[mode]); +} + +/** + * policy_mgr_clear_concurrency_mode() - To clear concurrency mode + * @psoc: PSOC object data + * @mode: device mode + * + * This routine is called to clear the concurrency mode + * + * Return: NONE + */ +void policy_mgr_clear_concurrency_mode(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return; + } + + switch (mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_P2P_GO_MODE: + case QDF_SAP_MODE: + case QDF_MONITOR_MODE: + pm_ctx->no_of_open_sessions[mode]--; + if (!(pm_ctx->no_of_open_sessions[mode])) + pm_ctx->concurrency_mode &= (~(1 << mode)); + break; + default: + break; + } + + policy_mgr_info("concurrency_mode = 0x%x Number of open sessions for mode %d = %d", + pm_ctx->concurrency_mode, mode, + pm_ctx->no_of_open_sessions[mode]); +} + +void policy_mgr_incr_active_session(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode, + uint8_t session_id) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + /* + * Need to aquire mutex as entire functionality in this function + * is in critical section + */ + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + switch (mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_P2P_GO_MODE: + case QDF_SAP_MODE: + case QDF_IBSS_MODE: + pm_ctx->no_of_active_sessions[mode]++; + break; + default: + break; + } + + if (pm_ctx->dp_cbacks.hdd_v2_flow_pool_map) + pm_ctx->dp_cbacks.hdd_v2_flow_pool_map(session_id); + + policy_mgr_debug("No.# of active sessions for mode %d = %d", + mode, pm_ctx->no_of_active_sessions[mode]); + policy_mgr_incr_connection_count(psoc, session_id); + if ((policy_mgr_mode_specific_connection_count( + psoc, PM_STA_MODE, NULL) > 0) && (mode != QDF_STA_MODE)) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_set_pcl_for_existing_combo(psoc, PM_STA_MODE); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + } + + /* Notify tdls */ + if (pm_ctx->tdls_cbacks.tdls_notify_increment_session) + pm_ctx->tdls_cbacks.tdls_notify_increment_session(psoc); + + /* + * Disable LRO/GRO if P2P or IBSS or SAP connection has come up or + * there are more than one STA connections + */ + if ((policy_mgr_mode_specific_connection_count(psoc, PM_STA_MODE, NULL) > 1) || + (policy_mgr_mode_specific_connection_count(psoc, PM_SAP_MODE, NULL) > 0) || + (policy_mgr_mode_specific_connection_count(psoc, PM_P2P_CLIENT_MODE, NULL) > + 0) || + (policy_mgr_mode_specific_connection_count(psoc, PM_P2P_GO_MODE, NULL) > 0) || + (policy_mgr_mode_specific_connection_count(psoc, PM_IBSS_MODE, NULL) > 0)) { + if (pm_ctx->dp_cbacks.hdd_disable_rx_ol_in_concurrency != NULL) + pm_ctx->dp_cbacks.hdd_disable_rx_ol_in_concurrency(true); + }; + + /* Enable RPS if SAP interface has come up */ + if (policy_mgr_mode_specific_connection_count(psoc, PM_SAP_MODE, NULL) + == 1) { + if (pm_ctx->dp_cbacks.hdd_set_rx_mode_rps_cb != NULL) + pm_ctx->dp_cbacks.hdd_set_rx_mode_rps_cb(true); + } + + policy_mgr_dump_current_concurrency(psoc); + + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); +} + +QDF_STATUS policy_mgr_decr_active_session(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode, + uint8_t session_id) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + QDF_STATUS qdf_status; + bool mcc_mode; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("context is NULL"); + return QDF_STATUS_E_EMPTY; + } + + qdf_status = policy_mgr_check_conn_with_mode_and_vdev_id(psoc, + policy_mgr_convert_device_mode_to_qdf_type(mode), + session_id); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + policy_mgr_debug("No connection with mode:%d vdev_id:%d", + policy_mgr_convert_device_mode_to_qdf_type(mode), + session_id); + return qdf_status; + } + + switch (mode) { + case QDF_STA_MODE: + case QDF_P2P_CLIENT_MODE: + case QDF_P2P_GO_MODE: + case QDF_SAP_MODE: + case QDF_IBSS_MODE: + if (pm_ctx->no_of_active_sessions[mode]) + pm_ctx->no_of_active_sessions[mode]--; + break; + default: + break; + } + + if (pm_ctx->dp_cbacks.hdd_v2_flow_pool_unmap) + pm_ctx->dp_cbacks.hdd_v2_flow_pool_unmap(session_id); + + policy_mgr_debug("No.# of active sessions for mode %d = %d", + mode, pm_ctx->no_of_active_sessions[mode]); + + policy_mgr_decr_connection_count(psoc, session_id); + + /* Notify tdls */ + if (pm_ctx->tdls_cbacks.tdls_notify_decrement_session) + pm_ctx->tdls_cbacks.tdls_notify_decrement_session(psoc); + /* Enable LRO/GRO if there no concurrency */ + if ((policy_mgr_mode_specific_connection_count(psoc, PM_STA_MODE, NULL) == 1) && + (policy_mgr_mode_specific_connection_count(psoc, PM_SAP_MODE, NULL) == 0) && + (policy_mgr_mode_specific_connection_count(psoc, PM_P2P_CLIENT_MODE, NULL) == + 0) && + (policy_mgr_mode_specific_connection_count(psoc, PM_P2P_GO_MODE, NULL) == 0) && + (policy_mgr_mode_specific_connection_count(psoc, PM_IBSS_MODE, NULL) == 0)) { + if (pm_ctx->dp_cbacks.hdd_disable_rx_ol_in_concurrency != NULL) + pm_ctx->dp_cbacks.hdd_disable_rx_ol_in_concurrency(false); + }; + + /* Disable RPS if SAP interface has come up */ + if (policy_mgr_mode_specific_connection_count(psoc, PM_SAP_MODE, NULL) + == 0) { + if (pm_ctx->dp_cbacks.hdd_set_rx_mode_rps_cb != NULL) + pm_ctx->dp_cbacks.hdd_set_rx_mode_rps_cb(false); + } + + policy_mgr_dump_current_concurrency(psoc); + + /* + * Check mode of entry being removed. Update mcc_mode only when STA + * or SAP since IPA only cares about these two + */ + if (mode == QDF_STA_MODE || mode == QDF_SAP_MODE) { + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + mcc_mode = policy_mgr_current_concurrency_is_mcc(psoc); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + if (pm_ctx->dp_cbacks.hdd_ipa_set_mcc_mode_cb) + pm_ctx->dp_cbacks.hdd_ipa_set_mcc_mode_cb(mcc_mode); + } + + return qdf_status; +} + +QDF_STATUS policy_mgr_incr_connection_count( + struct wlan_objmgr_psoc *psoc, uint32_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t conn_index; + struct policy_mgr_vdev_entry_info conn_table_entry; + enum policy_mgr_chain_mode chain_mask = POLICY_MGR_ONE_ONE; + uint8_t nss_2g = 0, nss_5g = 0; + enum policy_mgr_con_mode mode; + uint8_t chan; + uint32_t nss = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool update_conn = true; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("context is NULL"); + return status; + } + + conn_index = policy_mgr_get_connection_count(psoc); + if (pm_ctx->cfg.max_conc_cxns < conn_index) { + policy_mgr_err("exceeded max connection limit %d", + pm_ctx->cfg.max_conc_cxns); + return status; + } + if (pm_ctx->wma_cbacks.wma_get_connection_info) { + status = pm_ctx->wma_cbacks.wma_get_connection_info( + vdev_id, &conn_table_entry); + if (QDF_STATUS_SUCCESS != status) { + policy_mgr_err("can't find vdev_id %d in connection table", + vdev_id); + return status; + } + } else { + policy_mgr_err("wma_get_connection_info is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mode = policy_mgr_get_mode(conn_table_entry.type, + conn_table_entry.sub_type); + chan = wlan_reg_freq_to_chan(pm_ctx->pdev, conn_table_entry.mhz); + status = policy_mgr_get_nss_for_vdev(psoc, mode, &nss_2g, &nss_5g); + if (QDF_IS_STATUS_SUCCESS(status)) { + if ((WLAN_REG_IS_24GHZ_CH(chan) && (nss_2g > 1)) || + (WLAN_REG_IS_5GHZ_CH(chan) && (nss_5g > 1))) + chain_mask = POLICY_MGR_TWO_TWO; + else + chain_mask = POLICY_MGR_ONE_ONE; + nss = (WLAN_REG_IS_24GHZ_CH(chan)) ? nss_2g : nss_5g; + } else { + policy_mgr_err("Error in getting nss"); + } + + if (mode == PM_STA_MODE || mode == PM_P2P_CLIENT_MODE) + update_conn = false; + + /* add the entry */ + policy_mgr_update_conc_list(psoc, conn_index, + mode, + chan, + policy_mgr_get_bw(conn_table_entry.chan_width), + conn_table_entry.mac_id, + chain_mask, + nss, vdev_id, true, update_conn); + policy_mgr_debug("Add at idx:%d vdev %d mac=%d", + conn_index, vdev_id, + conn_table_entry.mac_id); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_decr_connection_count(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t conn_index = 0, next_conn_index = 0; + bool found = false; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return status; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + if (vdev_id == pm_conc_connection_list[conn_index].vdev_id) { + /* debug msg */ + found = true; + break; + } + conn_index++; + } + if (!found) { + policy_mgr_err("can't find vdev_id %d in pm_conc_connection_list", + vdev_id); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + return status; + } + next_conn_index = conn_index + 1; + while (PM_CONC_CONNECTION_LIST_VALID_INDEX(next_conn_index)) { + pm_conc_connection_list[conn_index].vdev_id = + pm_conc_connection_list[next_conn_index].vdev_id; + pm_conc_connection_list[conn_index].mode = + pm_conc_connection_list[next_conn_index].mode; + pm_conc_connection_list[conn_index].mac = + pm_conc_connection_list[next_conn_index].mac; + pm_conc_connection_list[conn_index].chan = + pm_conc_connection_list[next_conn_index].chan; + pm_conc_connection_list[conn_index].bw = + pm_conc_connection_list[next_conn_index].bw; + pm_conc_connection_list[conn_index].chain_mask = + pm_conc_connection_list[next_conn_index].chain_mask; + pm_conc_connection_list[conn_index].original_nss = + pm_conc_connection_list[next_conn_index].original_nss; + pm_conc_connection_list[conn_index].in_use = + pm_conc_connection_list[next_conn_index].in_use; + conn_index++; + next_conn_index++; + } + + /* clean up the entry */ + qdf_mem_zero(&pm_conc_connection_list[next_conn_index - 1], + sizeof(*pm_conc_connection_list)); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return QDF_STATUS_SUCCESS; +} + +bool policy_mgr_map_concurrency_mode(enum QDF_OPMODE *old_mode, + enum policy_mgr_con_mode *new_mode) +{ + bool status = true; + + switch (*old_mode) { + + case QDF_STA_MODE: + *new_mode = PM_STA_MODE; + break; + case QDF_SAP_MODE: + *new_mode = PM_SAP_MODE; + break; + case QDF_P2P_CLIENT_MODE: + *new_mode = PM_P2P_CLIENT_MODE; + break; + case QDF_P2P_GO_MODE: + *new_mode = PM_P2P_GO_MODE; + break; + case QDF_IBSS_MODE: + *new_mode = PM_IBSS_MODE; + break; + default: + *new_mode = PM_MAX_NUM_OF_MODE; + status = false; + break; + } + + return status; +} + +bool policy_mgr_is_ibss_conn_exist(struct wlan_objmgr_psoc *psoc, + uint8_t *ibss_channel) +{ + uint32_t count = 0, index = 0; + uint32_t list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + bool status = false; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return status; + } + if (NULL == ibss_channel) { + policy_mgr_err("Null pointer error"); + return false; + } + count = policy_mgr_mode_specific_connection_count( + psoc, PM_IBSS_MODE, list); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (count == 0) { + /* No IBSS connection */ + status = false; + } else if (count == 1) { + *ibss_channel = pm_conc_connection_list[list[index]].chan; + status = true; + } else { + *ibss_channel = pm_conc_connection_list[list[index]].chan; + policy_mgr_debug("Multiple IBSS connections, picking first one"); + status = true; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return status; +} + +uint32_t policy_mgr_get_mode_specific_conn_info(struct wlan_objmgr_psoc *psoc, + uint8_t *channel, uint8_t *vdev_id, + enum policy_mgr_con_mode mode) +{ + + uint32_t count = 0, index = 0; + uint32_t list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return count; + } + if (NULL == channel || NULL == vdev_id) { + policy_mgr_err("Null pointer error"); + return count; + } + + count = policy_mgr_mode_specific_connection_count( + psoc, mode, list); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (count == 0) { + policy_mgr_debug("No mode:[%d] connection", mode); + } else if (count == 1) { + *channel = pm_conc_connection_list[list[index]].chan; + *vdev_id = + pm_conc_connection_list[list[index]].vdev_id; + } else { + for (index = 0; index < count; index++) { + channel[index] = + pm_conc_connection_list[list[index]].chan; + + vdev_id[index] = + pm_conc_connection_list[list[index]].vdev_id; + } + policy_mgr_debug("Multiple mode:[%d] connections", mode); + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return count; +} + +bool policy_mgr_max_concurrent_connections_reached( + struct wlan_objmgr_psoc *psoc) +{ + uint8_t i = 0, j = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (NULL != pm_ctx) { + for (i = 0; i < QDF_MAX_NO_OF_MODE; i++) + j += pm_ctx->no_of_active_sessions[i]; + return j > + (pm_ctx->cfg.max_conc_cxns - 1); + } + + return false; +} + +static bool policy_mgr_is_sub_20_mhz_enabled(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + return pm_ctx->user_cfg.sub_20_mhz_enabled; +} + +/** + * policy_mgr_check_privacy_for_new_conn() - Check privacy mode concurrency + * @pm_ctx: policy_mgr_psoc_priv_obj policy mgr context + * + * This routine is called to check vdev security mode allowed in concurrency. + * At present, WAPI security mode is not allowed to run concurrency with any + * other vdev. + * + * Return: true - allow + */ +static bool policy_mgr_check_privacy_for_new_conn( + struct policy_mgr_psoc_priv_obj *pm_ctx) +{ + if (!pm_ctx->hdd_cbacks.hdd_wapi_security_sta_exist) + return true; + + if (pm_ctx->hdd_cbacks.hdd_wapi_security_sta_exist() && + (policy_mgr_get_connection_count(pm_ctx->psoc) > 0)) + return false; + + return true; +} + +bool policy_mgr_is_concurrency_allowed(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint8_t channel, + enum hw_mode_bandwidth bw) +{ + uint32_t num_connections = 0, count = 0, index = 0; + bool status = false, match = false; + uint32_t list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool sta_sap_scc_on_dfs_chan; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return status; + } + /* find the current connection state from pm_conc_connection_list*/ + num_connections = policy_mgr_get_connection_count(psoc); + + if (num_connections && policy_mgr_is_sub_20_mhz_enabled(psoc)) { + policy_mgr_err("dont allow concurrency if Sub 20 MHz is enabled"); + status = false; + goto done; + } + + if (policy_mgr_max_concurrent_connections_reached(psoc)) { + policy_mgr_err("Reached max concurrent connections: %d", + pm_ctx->cfg.max_conc_cxns); + goto done; + } + + if (channel) { + /* don't allow 3rd home channel on same MAC */ + if (!policy_mgr_allow_new_home_channel(psoc, + channel, num_connections)) + goto done; + + /* + * 1) DFS MCC is not yet supported + * 2) If you already have STA connection on 5G channel then + * don't allow any other persona to make connection on DFS + * channel because STA 5G + DFS MCC is not allowed. + * 3) If STA is on 2G channel and SAP is coming up on + * DFS channel then allow concurrency but make sure it is + * going to DBS and send PCL to firmware indicating that + * don't allow STA to roam to 5G channels. + */ + if (!policy_mgr_is_5g_channel_allowed(psoc, + channel, list, PM_P2P_GO_MODE)) + goto done; + if (!policy_mgr_is_5g_channel_allowed(psoc, + channel, list, PM_SAP_MODE)) + goto done; + + sta_sap_scc_on_dfs_chan = + policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc); + policy_mgr_debug("sta_sap_scc_on_dfs_chan %u", + sta_sap_scc_on_dfs_chan); + + if (!sta_sap_scc_on_dfs_chan && ((mode == PM_P2P_GO_MODE) || + (mode == PM_SAP_MODE))) { + if (wlan_reg_is_dfs_ch(pm_ctx->pdev, channel)) + match = policy_mgr_disallow_mcc(psoc, channel); + } + if (true == match) { + policy_mgr_err("No MCC, SAP/GO about to come up on DFS channel"); + goto done; + } + } + + /* + * Check all IBSS+STA concurrencies + * + * don't allow IBSS + STA MCC + * don't allow IBSS + STA SCC if IBSS is on DFS channel + */ + count = policy_mgr_mode_specific_connection_count(psoc, + PM_STA_MODE, list); + if ((PM_IBSS_MODE == mode) && + (policy_mgr_mode_specific_connection_count(psoc, + PM_IBSS_MODE, list)) && count) { + policy_mgr_err("No 2nd IBSS, we already have STA + IBSS"); + goto done; + } + if ((PM_IBSS_MODE == mode) && + (wlan_reg_is_dfs_ch(pm_ctx->pdev, channel)) && count) { + policy_mgr_err("No IBSS + STA SCC/MCC, IBSS is on DFS channel"); + goto done; + } + if (PM_IBSS_MODE == mode) { + if (policy_mgr_is_hw_dbs_capable(psoc) == true) { + if (num_connections > 1) { + policy_mgr_err("No IBSS, we have concurrent connections already"); + goto done; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (PM_STA_MODE != pm_conc_connection_list[0].mode) { + policy_mgr_err("No IBSS, we've a non-STA connection"); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + goto done; + } + /* + * This logic protects STA and IBSS to come up on same + * band. If requirement changes then this condition + * needs to be removed + */ + if (channel && + (pm_conc_connection_list[0].chan != channel) && + WLAN_REG_IS_SAME_BAND_CHANNELS( + pm_conc_connection_list[0].chan, channel)) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_err("No IBSS + STA MCC"); + goto done; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + } else if (num_connections) { + policy_mgr_err("No IBSS, we have one connection already"); + goto done; + } + } + + if ((PM_STA_MODE == mode) && + (policy_mgr_mode_specific_connection_count(psoc, + PM_IBSS_MODE, list)) && count) { + policy_mgr_err("No 2nd STA, we already have STA + IBSS"); + goto done; + } + + if ((PM_STA_MODE == mode) && + (policy_mgr_mode_specific_connection_count(psoc, + PM_IBSS_MODE, list))) { + if (policy_mgr_is_hw_dbs_capable(psoc) == true) { + if (num_connections > 1) { + policy_mgr_err("No 2nd STA, we already have IBSS concurrency"); + goto done; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (channel && + (wlan_reg_is_dfs_ch(pm_ctx->pdev, + pm_conc_connection_list[0].chan)) + && (WLAN_REG_IS_5GHZ_CH(channel))) { + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + policy_mgr_err("No IBSS + STA SCC/MCC, IBSS is on DFS channel"); + goto done; + } + /* + * This logic protects STA and IBSS to come up on same + * band. If requirement changes then this condition + * needs to be removed + */ + if ((pm_conc_connection_list[0].chan != channel) && + WLAN_REG_IS_SAME_BAND_CHANNELS( + pm_conc_connection_list[0].chan, channel)) { + policy_mgr_err("No IBSS + STA MCC"); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + goto done; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + } else { + policy_mgr_err("No STA, we have IBSS connection already"); + goto done; + } + } + + if (!policy_mgr_allow_sap_go_concurrency(psoc, mode, channel, + WLAN_INVALID_VDEV_ID)) { + policy_mgr_err("This concurrency combination is not allowed"); + goto done; + } + + /* don't allow two P2P GO on same band */ + if (channel && (mode == PM_P2P_GO_MODE) && num_connections) { + index = 0; + count = policy_mgr_mode_specific_connection_count(psoc, + PM_P2P_GO_MODE, list); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + while (index < count) { + if (WLAN_REG_IS_SAME_BAND_CHANNELS(channel, + pm_conc_connection_list[list[index]].chan)) { + policy_mgr_err("Don't allow P2P GO on same band"); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + goto done; + } + index++; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + } + + if (!policy_mgr_check_privacy_for_new_conn(pm_ctx)) { + policy_mgr_err("Don't allow new conn when wapi security conn existing"); + goto done; + } + + status = true; + +done: + return status; +} + +bool policy_mgr_allow_concurrency(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint8_t channel, enum hw_mode_bandwidth bw) +{ + QDF_STATUS status; + struct policy_mgr_pcl_list pcl; + + qdf_mem_zero(&pcl, sizeof(pcl)); + status = policy_mgr_get_pcl(psoc, mode, pcl.pcl_list, &pcl.pcl_len, + pcl.weight_list, + QDF_ARRAY_SIZE(pcl.weight_list)); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("disallow connection:%d", status); + return false; + } + + return policy_mgr_is_concurrency_allowed(psoc, mode, channel, bw); +} + +bool policy_mgr_allow_concurrency_csa(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint8_t channel, + uint32_t vdev_id) +{ + bool allow = false; + struct policy_mgr_conc_connection_info info; + uint8_t num_cxn_del = 0; + + /* + * Store the connection's parameter and temporarily delete it + * from the concurrency table. This way the allow concurrency + * check can be used as though a new connection is coming up, + * after check, restore the connection to concurrency table. + */ + policy_mgr_store_and_del_conn_info_by_vdev_id(psoc, vdev_id, + &info, &num_cxn_del); + allow = policy_mgr_allow_concurrency( + psoc, + mode, + channel, + HW_MODE_20_MHZ); + if (!allow) + policy_mgr_err("CSA concurrency check failed"); + /* Restore the connection entry */ + if (num_cxn_del > 0) + policy_mgr_restore_deleted_conn_info(psoc, &info, num_cxn_del); + + return allow; +} + +/** + * policy_mgr_get_concurrency_mode() - return concurrency mode + * @psoc: PSOC object information + * + * This routine is used to retrieve concurrency mode + * + * Return: uint32_t value of concurrency mask + */ +uint32_t policy_mgr_get_concurrency_mode(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STA_MASK; + } + + policy_mgr_info("concurrency_mode: 0x%x", + pm_ctx->concurrency_mode); + + return pm_ctx->concurrency_mode; +} + +/** + * policy_mgr_get_channel_from_scan_result() - to get channel from scan result + * @psoc: PSOC object information + * @roam_profile: pointer to roam profile + * @channel: channel to be filled + * + * This routine gets channel which most likely a candidate to which STA + * will make connection. + * + * Return: QDF_STATUS + */ +QDF_STATUS policy_mgr_get_channel_from_scan_result( + struct wlan_objmgr_psoc *psoc, + void *roam_profile, uint8_t *channel) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + void *scan_cache = NULL; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_INVAL; + } + + if (!roam_profile || !channel) { + policy_mgr_err("Invalid input parameters"); + return QDF_STATUS_E_INVAL; + } + + if (pm_ctx->sme_cbacks.sme_get_ap_channel_from_scan) { + status = pm_ctx->sme_cbacks.sme_get_ap_channel_from_scan + (roam_profile, &scan_cache, channel); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Get AP channel failed"); + return status; + } + } else { + policy_mgr_err("sme_get_ap_channel_from_scan_cache NULL"); + return QDF_STATUS_E_FAILURE; + } + + if (pm_ctx->sme_cbacks.sme_scan_result_purge) + status = pm_ctx->sme_cbacks.sme_scan_result_purge(scan_cache); + else + policy_mgr_err("sme_scan_result_purge NULL"); + + return status; +} + +QDF_STATUS policy_mgr_set_user_cfg(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_user_cfg *user_cfg) +{ + + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + if (NULL == user_cfg) { + policy_mgr_err("Invalid User Config"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->user_cfg = *user_cfg; + policy_mgr_debug("dbs_selection_plcy 0x%x", + pm_ctx->cfg.dbs_selection_plcy); + policy_mgr_debug("vdev_priority_list 0x%x", + pm_ctx->cfg.vdev_priority_list); + pm_ctx->cur_conc_system_pref = pm_ctx->cfg.sys_pref; + + return QDF_STATUS_SUCCESS; +} + +uint8_t policy_mgr_search_and_check_for_session_conc( + struct wlan_objmgr_psoc *psoc, + uint8_t session_id, + void *roam_profile) +{ + uint8_t channel = 0; + QDF_STATUS status; + enum policy_mgr_con_mode mode; + bool ret; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return channel; + } + + if (pm_ctx->hdd_cbacks.get_mode_for_non_connected_vdev) { + mode = pm_ctx->hdd_cbacks.get_mode_for_non_connected_vdev( + psoc, session_id); + if (PM_MAX_NUM_OF_MODE == mode) { + policy_mgr_err("Invalid mode"); + return channel; + } + } else + return channel; + + status = policy_mgr_get_channel_from_scan_result(psoc, + roam_profile, &channel); + if ((QDF_STATUS_SUCCESS != status) || (channel == 0)) { + policy_mgr_err("%s error %d %d", + __func__, status, channel); + return 0; + } + + /* Take care of 160MHz and 80+80Mhz later */ + ret = policy_mgr_allow_concurrency(psoc, mode, channel, HW_MODE_20_MHZ); + if (false == ret) { + policy_mgr_err("Connection failed due to conc check fail"); + return 0; + } + + return channel; +} + +/** + * policy_mgr_is_two_connection_mcc() - Check if MCC scenario + * when there are two connections + * + * If if MCC scenario when there are two connections + * + * Return: true or false + */ +static bool policy_mgr_is_two_connection_mcc(void) +{ + return ((pm_conc_connection_list[0].chan != + pm_conc_connection_list[1].chan) && + (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) && + (pm_conc_connection_list[0].chan <= + WLAN_REG_MAX_24GHZ_CH_NUM) && + (pm_conc_connection_list[1].chan <= + WLAN_REG_MAX_24GHZ_CH_NUM)) ? true : false; +} + +/** + * policy_mgr_is_three_connection_mcc() - Check if MCC scenario + * when there are three connections + * + * If if MCC scenario when there are three connections + * + * Return: true or false + */ +static bool policy_mgr_is_three_connection_mcc(void) +{ + return (((pm_conc_connection_list[0].chan != + pm_conc_connection_list[1].chan) || + (pm_conc_connection_list[0].chan != + pm_conc_connection_list[2].chan) || + (pm_conc_connection_list[1].chan != + pm_conc_connection_list[2].chan)) && + (pm_conc_connection_list[0].chan <= + WLAN_REG_MAX_24GHZ_CH_NUM) && + (pm_conc_connection_list[1].chan <= + WLAN_REG_MAX_24GHZ_CH_NUM) && + (pm_conc_connection_list[2].chan <= + WLAN_REG_MAX_24GHZ_CH_NUM)) ? true : false; +} + +bool policy_mgr_is_mcc_in_24G(struct wlan_objmgr_psoc *psoc) +{ + uint32_t num_connections = 0; + bool is_24G_mcc = false; + + num_connections = policy_mgr_get_connection_count(psoc); + + switch (num_connections) { + case 1: + break; + case 2: + if (policy_mgr_is_two_connection_mcc()) + is_24G_mcc = true; + break; + case 3: + if (policy_mgr_is_three_connection_mcc()) + is_24G_mcc = true; + break; + default: + policy_mgr_err("unexpected num_connections value %d", + num_connections); + break; + } + + return is_24G_mcc; +} + +bool policy_mgr_check_for_session_conc(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, uint8_t channel) +{ + enum policy_mgr_con_mode mode; + bool ret; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + if (pm_ctx->hdd_cbacks.get_mode_for_non_connected_vdev) { + mode = pm_ctx->hdd_cbacks.get_mode_for_non_connected_vdev( + psoc, session_id); + if (PM_MAX_NUM_OF_MODE == mode) { + policy_mgr_err("Invalid mode"); + return false; + } + } else + return false; + + if (channel == 0) { + policy_mgr_err("Invalid channel number 0"); + return false; + } + + /* Take care of 160MHz and 80+80Mhz later */ + ret = policy_mgr_allow_concurrency(psoc, mode, channel, HW_MODE_20_MHZ); + if (false == ret) { + policy_mgr_err("Connection failed due to conc check fail"); + return 0; + } + + return true; +} + +/** + * policy_mgr_change_mcc_go_beacon_interval() - Change MCC beacon interval + * @psoc: PSOC object information + * @vdev_id: vdev id + * @dev_mode: device mode + * + * Updates the beacon parameters of the GO in MCC scenario + * + * Return: Success or Failure depending on the overall function behavior + */ +QDF_STATUS policy_mgr_change_mcc_go_beacon_interval( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id, enum QDF_OPMODE dev_mode) +{ + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + policy_mgr_info("UPDATE Beacon Params"); + + if (QDF_SAP_MODE == dev_mode) { + if (pm_ctx->sme_cbacks.sme_change_mcc_beacon_interval + ) { + status = pm_ctx->sme_cbacks. + sme_change_mcc_beacon_interval(vdev_id); + if (status == QDF_STATUS_E_FAILURE) { + policy_mgr_err("Failed to update Beacon Params"); + return QDF_STATUS_E_FAILURE; + } + } else { + policy_mgr_err("sme_change_mcc_beacon_interval callback is NULL"); + return QDF_STATUS_E_FAILURE; + } + } + + return QDF_STATUS_SUCCESS; +} + +struct policy_mgr_conc_connection_info *policy_mgr_get_conn_info(uint32_t *len) +{ + struct policy_mgr_conc_connection_info *conn_ptr = + &pm_conc_connection_list[0]; + *len = MAX_NUMBER_OF_CONC_CONNECTIONS; + + return conn_ptr; +} + +enum policy_mgr_con_mode policy_mgr_convert_device_mode_to_qdf_type( + enum QDF_OPMODE device_mode) +{ + enum policy_mgr_con_mode mode = PM_MAX_NUM_OF_MODE; + switch (device_mode) { + case QDF_STA_MODE: + mode = PM_STA_MODE; + break; + case QDF_P2P_CLIENT_MODE: + mode = PM_P2P_CLIENT_MODE; + break; + case QDF_P2P_GO_MODE: + mode = PM_P2P_GO_MODE; + break; + case QDF_SAP_MODE: + mode = PM_SAP_MODE; + break; + case QDF_IBSS_MODE: + mode = PM_IBSS_MODE; + break; + default: + policy_mgr_debug("Unsupported mode (%d)", + device_mode); + } + + return mode; +} + +enum QDF_OPMODE policy_mgr_get_qdf_mode_from_pm( + enum policy_mgr_con_mode device_mode) +{ + enum QDF_OPMODE mode = QDF_MAX_NO_OF_MODE; + + switch (device_mode) { + case PM_STA_MODE: + mode = QDF_STA_MODE; + break; + case PM_SAP_MODE: + mode = QDF_SAP_MODE; + break; + case PM_P2P_CLIENT_MODE: + mode = QDF_P2P_CLIENT_MODE; + break; + case PM_P2P_GO_MODE: + mode = QDF_P2P_GO_MODE; + break; + case PM_IBSS_MODE: + mode = QDF_IBSS_MODE; + break; + default: + policy_mgr_debug("Unsupported policy mgr mode (%d)", + device_mode); + } + return mode; +} + +QDF_STATUS policy_mgr_mode_specific_num_open_sessions( + struct wlan_objmgr_psoc *psoc, enum QDF_OPMODE mode, + uint8_t *num_sessions) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + *num_sessions = pm_ctx->no_of_open_sessions[mode]; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_mode_specific_num_active_sessions( + struct wlan_objmgr_psoc *psoc, enum QDF_OPMODE mode, + uint8_t *num_sessions) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return QDF_STATUS_E_FAILURE; + } + + *num_sessions = pm_ctx->no_of_active_sessions[mode]; + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_concurrent_open_sessions_running() - Checks for + * concurrent open session + * @psoc: PSOC object information + * + * Checks if more than one open session is running for all the allowed modes + * in the driver + * + * Return: True if more than one open session exists, False otherwise + */ +bool policy_mgr_concurrent_open_sessions_running( + struct wlan_objmgr_psoc *psoc) +{ + uint8_t i = 0; + uint8_t j = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return false; + } + + for (i = 0; i < QDF_MAX_NO_OF_MODE; i++) + j += pm_ctx->no_of_open_sessions[i]; + + return j > 1; +} + +/** + * policy_mgr_concurrent_beaconing_sessions_running() - Checks + * for concurrent beaconing entities + * @psoc: PSOC object information + * + * Checks if multiple beaconing sessions are running i.e., if SAP or GO or IBSS + * are beaconing together + * + * Return: True if multiple entities are beaconing together, False otherwise + */ +bool policy_mgr_concurrent_beaconing_sessions_running( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid context"); + return false; + } + + return (pm_ctx->no_of_open_sessions[QDF_SAP_MODE] + + pm_ctx->no_of_open_sessions[QDF_P2P_GO_MODE] + + pm_ctx->no_of_open_sessions[QDF_IBSS_MODE] > 1) ? true : false; +} + + +void policy_mgr_clear_concurrent_session_count(struct wlan_objmgr_psoc *psoc) +{ + uint8_t i = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (NULL != pm_ctx) { + for (i = 0; i < QDF_MAX_NO_OF_MODE; i++) + pm_ctx->no_of_active_sessions[i] = 0; + } +} + +bool policy_mgr_is_multiple_active_sta_sessions(struct wlan_objmgr_psoc *psoc) +{ + return policy_mgr_mode_specific_connection_count( + psoc, PM_STA_MODE, NULL) > 1; +} + +/** + * policy_mgr_is_sta_active_connection_exists() - Check if a STA + * connection is active + * @psoc: PSOC object information + * + * Checks if there is atleast one active STA connection in the driver + * + * Return: True if an active STA session is present, False otherwise + */ +bool policy_mgr_is_sta_active_connection_exists( + struct wlan_objmgr_psoc *psoc) +{ + return (!policy_mgr_mode_specific_connection_count( + psoc, PM_STA_MODE, NULL)) ? false : true; +} + +bool policy_mgr_is_any_nondfs_chnl_present(struct wlan_objmgr_psoc *psoc, + uint8_t *channel) +{ + bool status = false; + uint32_t conn_index = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if (pm_conc_connection_list[conn_index].in_use && + !wlan_reg_is_dfs_ch(pm_ctx->pdev, + pm_conc_connection_list[conn_index].chan)) { + *channel = pm_conc_connection_list[conn_index].chan; + status = true; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return status; +} + +bool policy_mgr_is_any_dfs_beaconing_session_present( + struct wlan_objmgr_psoc *psoc, uint8_t *channel) +{ + struct policy_mgr_conc_connection_info *conn_info; + bool status = false; + uint32_t conn_index = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + conn_info = &pm_conc_connection_list[conn_index]; + if (conn_info->in_use && + wlan_reg_is_dfs_ch(pm_ctx->pdev, conn_info->chan) && + (PM_SAP_MODE == conn_info->mode || + PM_P2P_GO_MODE == conn_info->mode)) { + *channel = pm_conc_connection_list[conn_index].chan; + status = true; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return status; +} + +QDF_STATUS policy_mgr_get_nss_for_vdev(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint8_t *nss_2g, uint8_t *nss_5g) +{ + enum QDF_OPMODE dev_mode; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + dev_mode = policy_mgr_get_qdf_mode_from_pm(mode); + if (dev_mode == QDF_MAX_NO_OF_MODE) + return QDF_STATUS_E_FAILURE; + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + if (pm_ctx->sme_cbacks.sme_get_nss_for_vdev) { + pm_ctx->sme_cbacks.sme_get_nss_for_vdev( + dev_mode, nss_2g, nss_5g); + + } else { + policy_mgr_err("sme_get_nss_for_vdev callback is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +void policy_mgr_dump_connection_status_info(struct wlan_objmgr_psoc *psoc) +{ + uint32_t i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { + policy_mgr_debug("%d: use:%d vdev:%d mode:%d mac:%d chan:%d orig chainmask:%d orig nss:%d bw:%d", + i, pm_conc_connection_list[i].in_use, + pm_conc_connection_list[i].vdev_id, + pm_conc_connection_list[i].mode, + pm_conc_connection_list[i].mac, + pm_conc_connection_list[i].chan, + pm_conc_connection_list[i].chain_mask, + pm_conc_connection_list[i].original_nss, + pm_conc_connection_list[i].bw); + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); +} + +bool policy_mgr_is_any_mode_active_on_band_along_with_session( + struct wlan_objmgr_psoc *psoc, + uint8_t session_id, + enum policy_mgr_band band) +{ + uint32_t i; + bool status = false; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + status = false; + goto send_status; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { + switch (band) { + case POLICY_MGR_BAND_24: + if ((pm_conc_connection_list[i].vdev_id != session_id) + && (pm_conc_connection_list[i].in_use) && + (WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[i].chan))) { + status = true; + goto release_mutex_and_send_status; + } + break; + case POLICY_MGR_BAND_5: + if ((pm_conc_connection_list[i].vdev_id != session_id) + && (pm_conc_connection_list[i].in_use) && + (WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[i].chan))) { + status = true; + goto release_mutex_and_send_status; + } + break; + default: + policy_mgr_err("Invalidband option:%d", band); + status = false; + goto release_mutex_and_send_status; + } + } +release_mutex_and_send_status: + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); +send_status: + return status; +} + +QDF_STATUS policy_mgr_get_chan_by_session_id(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, uint8_t *chan) +{ + uint32_t i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { + if ((pm_conc_connection_list[i].vdev_id == session_id) && + (pm_conc_connection_list[i].in_use)) { + *chan = pm_conc_connection_list[i].chan; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + return QDF_STATUS_SUCCESS; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return QDF_STATUS_E_FAILURE; +} + +QDF_STATUS policy_mgr_get_mac_id_by_session_id(struct wlan_objmgr_psoc *psoc, + uint8_t session_id, uint8_t *mac_id) +{ + uint32_t i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { + if ((pm_conc_connection_list[i].vdev_id == session_id) && + (pm_conc_connection_list[i].in_use)) { + *mac_id = pm_conc_connection_list[i].mac; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + return QDF_STATUS_SUCCESS; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return QDF_STATUS_E_FAILURE; +} + +QDF_STATUS policy_mgr_get_mcc_session_id_on_mac(struct wlan_objmgr_psoc *psoc, + uint8_t mac_id, uint8_t session_id, + uint8_t *mcc_session_id) +{ + uint32_t i; + QDF_STATUS status; + uint8_t chan; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + status = policy_mgr_get_chan_by_session_id(psoc, session_id, &chan); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Failed to get channel for session id:%d", + session_id); + return QDF_STATUS_E_FAILURE; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (i = 0; i < MAX_NUMBER_OF_CONC_CONNECTIONS; i++) { + if (pm_conc_connection_list[i].mac != mac_id) + continue; + if (pm_conc_connection_list[i].vdev_id == session_id) + continue; + /* Inter band or intra band MCC */ + if ((pm_conc_connection_list[i].chan != chan) && + (pm_conc_connection_list[i].in_use)) { + *mcc_session_id = pm_conc_connection_list[i].vdev_id; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + return QDF_STATUS_SUCCESS; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return QDF_STATUS_E_FAILURE; +} + +uint8_t policy_mgr_get_mcc_operating_channel(struct wlan_objmgr_psoc *psoc, + uint8_t session_id) +{ + uint8_t mac_id, mcc_session_id; + QDF_STATUS status; + uint8_t chan; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return INVALID_CHANNEL_ID; + } + + status = policy_mgr_get_mac_id_by_session_id(psoc, session_id, &mac_id); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get MAC ID"); + return INVALID_CHANNEL_ID; + } + + status = policy_mgr_get_mcc_session_id_on_mac(psoc, mac_id, session_id, + &mcc_session_id); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get MCC session ID"); + return INVALID_CHANNEL_ID; + } + + status = policy_mgr_get_chan_by_session_id(psoc, mcc_session_id, + &chan); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Failed to get channel for MCC session ID:%d", + mcc_session_id); + return INVALID_CHANNEL_ID; + } + + return chan; +} + +void policy_mgr_set_do_hw_mode_change_flag(struct wlan_objmgr_psoc *psoc, + bool flag) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + pm_ctx->do_hw_mode_change = flag; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + policy_mgr_debug("hw_mode_change_channel:%d", flag); +} + +bool policy_mgr_is_hw_mode_change_after_vdev_up(struct wlan_objmgr_psoc *psoc) +{ + bool flag; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return INVALID_CHANNEL_ID; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + flag = pm_ctx->do_hw_mode_change; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return flag; +} + +bool policy_mgr_is_dnsc_set(struct wlan_objmgr_vdev *vdev) +{ + bool roffchan; + + if (!vdev) { + policy_mgr_err("Invalid parameter"); + return false; + } + + roffchan = wlan_vdev_mlme_cap_get(vdev, WLAN_VDEV_C_RESTRICT_OFFCHAN); + + policy_mgr_debug("Restrict offchannel:%s", + roffchan ? "set" : "clear"); + + return roffchan; +} + +QDF_STATUS policy_mgr_is_chan_ok_for_dnbs(struct wlan_objmgr_psoc *psoc, + uint8_t channel, bool *ok) +{ + uint32_t cc_count = 0, i; + uint8_t operating_channel[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint8_t vdev_id[MAX_NUMBER_OF_CONC_CONNECTIONS]; + struct wlan_objmgr_vdev *vdev; + + if (!channel || !ok) { + policy_mgr_err("Invalid parameter"); + return QDF_STATUS_E_INVAL; + } + + cc_count = policy_mgr_get_mode_specific_conn_info(psoc, + &operating_channel[cc_count], + &vdev_id[cc_count], + PM_SAP_MODE); + policy_mgr_debug("Number of SAP modes: %d", cc_count); + cc_count = cc_count + policy_mgr_get_mode_specific_conn_info(psoc, + &operating_channel[cc_count], + &vdev_id[cc_count], + PM_P2P_GO_MODE); + policy_mgr_debug("Number of beaconing entities (SAP + GO):%d", + cc_count); + if (!cc_count) { + *ok = true; + return QDF_STATUS_SUCCESS; + } + + for (i = 0; i < cc_count; i++) { + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + vdev_id[i], + WLAN_POLICY_MGR_ID); + if (!vdev) { + policy_mgr_err("vdev for vdev_id:%d is NULL", + vdev_id[i]); + return QDF_STATUS_E_INVAL; + } + + /** + * If channel passed is same as AP/GO operating channel, then + * return true. + * If channel is different from operating channel but in same band. + * return false. + * If operating channel in different band (DBS capable). + * return true. + * If operating channel in different band (not DBS capable). + * return false. + */ + /* TODO: To be enhanced for SBS */ + if (policy_mgr_is_dnsc_set(vdev)) { + if (operating_channel[i] == channel) { + *ok = true; + wlan_objmgr_vdev_release_ref(vdev, + WLAN_POLICY_MGR_ID); + break; + } else if (WLAN_REG_IS_SAME_BAND_CHANNELS( + operating_channel[i], channel)) { + *ok = false; + wlan_objmgr_vdev_release_ref(vdev, + WLAN_POLICY_MGR_ID); + break; + } else if (policy_mgr_is_hw_dbs_capable(psoc)) { + *ok = true; + wlan_objmgr_vdev_release_ref(vdev, + WLAN_POLICY_MGR_ID); + break; + } else { + *ok = false; + wlan_objmgr_vdev_release_ref(vdev, + WLAN_POLICY_MGR_ID); + break; + } + } else { + *ok = true; + } + wlan_objmgr_vdev_release_ref(vdev, WLAN_POLICY_MGR_ID); + } + policy_mgr_debug("chan: %d ok %d", channel, *ok); + + return QDF_STATUS_SUCCESS; +} + +uint32_t policy_mgr_get_hw_dbs_nss(struct wlan_objmgr_psoc *psoc, + struct dbs_nss *nss_dbs) +{ + int i, param; + uint32_t dbs, tx_chain0, rx_chain0, tx_chain1, rx_chain1; + uint32_t min_mac0_rf_chains, min_mac1_rf_chains; + uint32_t max_rf_chains, final_max_rf_chains = HW_MODE_SS_0x0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return final_max_rf_chains; + } + + for (i = 0; i < pm_ctx->num_dbs_hw_modes; i++) { + param = pm_ctx->hw_mode.hw_mode_list[i]; + dbs = POLICY_MGR_HW_MODE_DBS_MODE_GET(param); + + if (dbs) { + tx_chain0 + = POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_GET(param); + rx_chain0 + = POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_GET(param); + + tx_chain1 + = POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_GET(param); + rx_chain1 + = POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_GET(param); + + min_mac0_rf_chains = QDF_MIN(tx_chain0, rx_chain0); + min_mac1_rf_chains = QDF_MIN(tx_chain1, rx_chain1); + + max_rf_chains + = QDF_MAX(min_mac0_rf_chains, min_mac1_rf_chains); + + if (final_max_rf_chains < max_rf_chains) { + final_max_rf_chains + = (max_rf_chains == 2) + ? HW_MODE_SS_2x2 : HW_MODE_SS_1x1; + + nss_dbs->mac0_ss + = (min_mac0_rf_chains == 2) + ? HW_MODE_SS_2x2 : HW_MODE_SS_1x1; + + nss_dbs->mac1_ss + = (min_mac1_rf_chains == 2) + ? HW_MODE_SS_2x2 : HW_MODE_SS_1x1; + } + } else { + continue; + } + } + + return final_max_rf_chains; +} + +bool policy_mgr_is_scan_simultaneous_capable(struct wlan_objmgr_psoc *psoc) +{ + if ((DISABLE_DBS_CXN_AND_SCAN == + wlan_objmgr_psoc_get_dual_mac_disable(psoc)) || + (ENABLE_DBS_CXN_AND_DISABLE_DBS_SCAN == + wlan_objmgr_psoc_get_dual_mac_disable(psoc)) || + (ENABLE_DBS_CXN_AND_DISABLE_SIMULTANEOUS_SCAN == + wlan_objmgr_psoc_get_dual_mac_disable(psoc)) || + !policy_mgr_is_hw_dbs_capable(psoc)) + return false; + + return true; +} + +void policy_mgr_set_cur_conc_system_pref(struct wlan_objmgr_psoc *psoc, + uint8_t conc_system_pref) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + policy_mgr_debug("conc_system_pref %hu", conc_system_pref); + pm_ctx->cur_conc_system_pref = conc_system_pref; +} + +uint8_t policy_mgr_get_cur_conc_system_pref(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return PM_THROUGHPUT; + } + + policy_mgr_debug("conc_system_pref %hu", pm_ctx->cur_conc_system_pref); + return pm_ctx->cur_conc_system_pref; +} + +QDF_STATUS policy_mgr_get_updated_scan_and_fw_mode_config( + struct wlan_objmgr_psoc *psoc, uint32_t *scan_config, + uint32_t *fw_mode_config, uint32_t dual_mac_disable_ini, + uint32_t channel_select_logic_conc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + *scan_config = pm_ctx->dual_mac_cfg.cur_scan_config; + *fw_mode_config = pm_ctx->dual_mac_cfg.cur_fw_mode_config; + switch (dual_mac_disable_ini) { + case DISABLE_DBS_CXN_AND_ENABLE_DBS_SCAN_WITH_ASYNC_SCAN_OFF: + policy_mgr_debug("dual_mac_disable_ini:%d async/dbs off", + dual_mac_disable_ini); + WMI_DBS_CONC_SCAN_CFG_ASYNC_DBS_SCAN_SET(*scan_config, 0); + WMI_DBS_FW_MODE_CFG_DBS_FOR_CXN_SET(*fw_mode_config, 0); + break; + case DISABLE_DBS_CXN_AND_ENABLE_DBS_SCAN: + policy_mgr_debug("dual_mac_disable_ini:%d dbs_cxn off", + dual_mac_disable_ini); + WMI_DBS_FW_MODE_CFG_DBS_FOR_CXN_SET(*fw_mode_config, 0); + break; + case ENABLE_DBS_CXN_AND_ENABLE_SCAN_WITH_ASYNC_SCAN_OFF: + policy_mgr_debug("dual_mac_disable_ini:%d async off", + dual_mac_disable_ini); + WMI_DBS_CONC_SCAN_CFG_ASYNC_DBS_SCAN_SET(*scan_config, 0); + break; + case ENABLE_DBS_CXN_AND_DISABLE_DBS_SCAN: + policy_mgr_debug("%s: dual_mac_disable_ini:%d ", __func__, + dual_mac_disable_ini); + WMI_DBS_CONC_SCAN_CFG_DBS_SCAN_SET(*scan_config, 0); + break; + default: + break; + } + + WMI_DBS_FW_MODE_CFG_DBS_FOR_STA_PLUS_STA_SET(*fw_mode_config, + PM_CHANNEL_SELECT_LOGIC_STA_STA_GET(channel_select_logic_conc)); + WMI_DBS_FW_MODE_CFG_DBS_FOR_STA_PLUS_P2P_SET(*fw_mode_config, + PM_CHANNEL_SELECT_LOGIC_STA_P2P_GET(channel_select_logic_conc)); + + policy_mgr_debug("*scan_config:%x ", *scan_config); + policy_mgr_debug("*fw_mode_config:%x ", *fw_mode_config); + + return QDF_STATUS_SUCCESS; +} + +bool policy_mgr_is_force_scc(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return 0; + } + + return ((pm_ctx->cfg.mcc_to_scc_switch == + QDF_MCC_TO_SCC_SWITCH_FORCE_WITHOUT_DISCONNECTION) || + (pm_ctx->cfg.mcc_to_scc_switch == + QDF_MCC_TO_SCC_SWITCH_WITH_FAVORITE_CHANNEL) || + (pm_ctx->cfg.mcc_to_scc_switch == + QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION) || + (pm_ctx->cfg.mcc_to_scc_switch == + QDF_MCC_TO_SCC_WITH_PREFERRED_BAND)); +} + +bool policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool status = false; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return status; + } + + if (policy_mgr_is_force_scc(psoc) && + pm_ctx->user_cfg.is_sta_sap_scc_allowed_on_dfs_chan) + status = true; + + return status; +} + +bool policy_mgr_is_sta_connected_2g(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t conn_index; + bool ret = false; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return ret; + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if (pm_conc_connection_list[conn_index].mode == PM_STA_MODE && + pm_conc_connection_list[conn_index].chan <= 14 && + pm_conc_connection_list[conn_index].in_use) + ret = true; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return ret; +} + +void policy_mgr_trim_acs_channel_list(struct wlan_objmgr_psoc *psoc, + uint8_t *org_ch_list, uint8_t *org_ch_list_count) +{ + uint32_t list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + uint32_t index = 0, count, i, ch_list_count; + uint8_t band_mask = 0, ch_5g = 0, ch_24g = 0; + uint8_t ch_list[QDF_MAX_NUM_CHAN]; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + if (*org_ch_list_count >= QDF_MAX_NUM_CHAN) { + policy_mgr_err("org_ch_list_count too big %d", + *org_ch_list_count); + return; + } + /* + * if force SCC is enabled and there is a STA connection, trim the + * ACS channel list on the band on which STA connection is present + */ + count = policy_mgr_mode_specific_connection_count( + psoc, PM_STA_MODE, list); + if (!(policy_mgr_is_force_scc(psoc) && count)) + return; + while (index < count) { + if (WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[list[index]].chan) && + policy_mgr_is_safe_channel(psoc, + pm_conc_connection_list[list[index]].chan)) { + band_mask |= 1; + ch_24g = pm_conc_connection_list[list[index]].chan; + } + if (WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[list[index]].chan) && + policy_mgr_is_safe_channel(psoc, + pm_conc_connection_list[list[index]].chan) && + !wlan_reg_is_dfs_ch(pm_ctx->pdev, + pm_conc_connection_list[list[index]].chan) && + !wlan_reg_is_passive_or_disable_ch(pm_ctx->pdev, + pm_conc_connection_list[list[index]].chan)) { + band_mask |= 2; + ch_5g = pm_conc_connection_list[list[index]].chan; + } + index++; + } + ch_list_count = 0; + if (band_mask == 1) { + ch_list[ch_list_count++] = ch_24g; + for (i = 0; i < *org_ch_list_count; i++) { + if (WLAN_REG_IS_24GHZ_CH( + org_ch_list[i])) + continue; + ch_list[ch_list_count++] = + org_ch_list[i]; + } + } else if (band_mask == 2) { + if ((reg_get_channel_state(pm_ctx->pdev, ch_5g) == + CHANNEL_STATE_DFS) && + policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc)) + ch_list[ch_list_count++] = ch_5g; + else if (!(reg_get_channel_state(pm_ctx->pdev, ch_5g) == + CHANNEL_STATE_DFS)) + ch_list[ch_list_count++] = ch_5g; + for (i = 0; i < *org_ch_list_count; i++) { + if (WLAN_REG_IS_5GHZ_CH( + org_ch_list[i])) + continue; + ch_list[ch_list_count++] = + org_ch_list[i]; + } + } else if (band_mask == 3) { + ch_list[ch_list_count++] = ch_24g; + ch_list[ch_list_count++] = ch_5g; + } else { + policy_mgr_debug("unexpected band_mask value %d", + band_mask); + return; + } + + *org_ch_list_count = ch_list_count; + for (i = 0; i < *org_ch_list_count; i++) + org_ch_list[i] = ch_list[i]; + +} + +uint32_t policy_mgr_get_connection_info(struct wlan_objmgr_psoc *psoc, + struct connection_info *info) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t conn_index, count = 0; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return count; + } + + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if (PM_CONC_CONNECTION_LIST_VALID_INDEX(conn_index)) { + info[count].vdev_id = + pm_conc_connection_list[conn_index].vdev_id; + info[count].mac_id = + pm_conc_connection_list[conn_index].mac; + info[count].channel = + pm_conc_connection_list[conn_index].chan; + count++; + } + } + + return count; +} + +bool policy_mgr_allow_sap_go_concurrency(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint8_t channel, + uint32_t vdev_id) +{ + enum policy_mgr_con_mode con_mode; + uint8_t con_chan; + int id; + uint32_t vdev; + bool dbs; + + if (mode != PM_SAP_MODE && mode != PM_P2P_GO_MODE) + return true; + if (policy_mgr_dual_beacon_on_single_mac_mcc_capable(psoc)) + return true; + dbs = policy_mgr_is_hw_dbs_capable(psoc); + for (id = 0; id < MAX_NUMBER_OF_CONC_CONNECTIONS; id++) { + if (!pm_conc_connection_list[id].in_use) + continue; + vdev = pm_conc_connection_list[id].vdev_id; + if (vdev_id == vdev) + continue; + con_mode = pm_conc_connection_list[id].mode; + if (con_mode != PM_SAP_MODE && con_mode != PM_P2P_GO_MODE) + continue; + con_chan = pm_conc_connection_list[id].chan; + if (policy_mgr_dual_beacon_on_single_mac_scc_capable(psoc) && + (channel == con_chan)) { + policy_mgr_debug("SCC enabled, 2 AP on same channel, allow 2nd AP"); + return true; + } + if (!dbs) { + policy_mgr_debug("DBS unsupported, mcc and scc unsupported too, don't allow 2nd AP"); + return false; + } + if (WLAN_REG_IS_SAME_BAND_CHANNELS(channel, con_chan)) { + policy_mgr_debug("DBS supported, 2 SAP on same band, reject 2nd AP"); + return false; + } + } + + /* Don't block the second interface */ + return true; +} + +bool policy_mgr_dual_beacon_on_single_mac_scc_capable( + struct wlan_objmgr_psoc *psoc) +{ + struct wmi_unified *wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + policy_mgr_debug("Invalid WMI handle"); + return false; + } + + if (wmi_service_enabled( + wmi_handle, + wmi_service_dual_beacon_on_single_mac_scc_support)) { + policy_mgr_debug("Dual beaconing on same channel on single MAC supported"); + return true; + } + policy_mgr_debug("Dual beaconing on same channel on single MAC is not supported"); + return false; +} + +bool policy_mgr_dual_beacon_on_single_mac_mcc_capable( + struct wlan_objmgr_psoc *psoc) +{ + struct wmi_unified *wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + policy_mgr_debug("Invalid WMI handle"); + return false; + } + + if (wmi_service_enabled( + wmi_handle, + wmi_service_dual_beacon_on_single_mac_mcc_support)) { + policy_mgr_debug("Dual beaconing on different channel on single MAC supported"); + return true; + } + policy_mgr_debug("Dual beaconing on different channel on single MAC is not supported"); + return false; +} + +bool policy_mgr_sta_sap_scc_on_lte_coex_chan( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + return pm_ctx->user_cfg.sta_sap_scc_on_lte_coex_chan; +} + +bool policy_mgr_is_valid_for_channel_switch(struct wlan_objmgr_psoc *psoc, + uint8_t channel) +{ + uint32_t sta_sap_scc_on_dfs_chan; + uint32_t sap_count; + enum channel_state state; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + sta_sap_scc_on_dfs_chan = + policy_mgr_is_sta_sap_scc_allowed_on_dfs_chan(psoc); + sap_count = policy_mgr_mode_specific_connection_count(psoc, + PM_SAP_MODE, + NULL); + state = reg_get_channel_state(pm_ctx->pdev, channel); + + policy_mgr_debug("sta_sap_scc_on_dfs_chan %u, sap_count %u, channel %u, state %u", + sta_sap_scc_on_dfs_chan, sap_count, channel, state); + + if ((state == CHANNEL_STATE_ENABLE) || (sap_count == 0) || + ((state == CHANNEL_STATE_DFS) && sta_sap_scc_on_dfs_chan)) { + policy_mgr_debug("Valid channel for channel switch"); + return true; + } + + policy_mgr_debug("Invalid channel for channel switch"); + return false; +} + +bool policy_mgr_is_sta_sap_scc(struct wlan_objmgr_psoc *psoc, uint8_t sap_ch) +{ + uint32_t conn_index; + bool is_scc = false; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return is_scc; + } + + if (!policy_mgr_mode_specific_connection_count( + psoc, PM_STA_MODE, NULL)) { + policy_mgr_debug("There is no STA+SAP conc"); + return is_scc; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if (pm_conc_connection_list[conn_index].in_use && + (pm_conc_connection_list[conn_index].mode == + PM_STA_MODE) && + (sap_ch == pm_conc_connection_list[conn_index].chan)) { + is_scc = true; + break; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return is_scc; +} diff --git a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_i.h b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_i.h new file mode 100644 index 000000000000..5f29c534f726 --- /dev/null +++ b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_i.h @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WLAN_POLICY_MGR_I_H +#define WLAN_POLICY_MGR_I_H + +#include "wlan_policy_mgr_api.h" +#include "qdf_event.h" +#include "qdf_mc_timer.h" +#include "qdf_lock.h" +#include "qdf_defer.h" +#include "wlan_reg_services_api.h" + +#define DBS_OPPORTUNISTIC_TIME 5 +#ifdef QCA_WIFI_3_0_EMU +#define CONNECTION_UPDATE_TIMEOUT 3000 +#else +#define CONNECTION_UPDATE_TIMEOUT 1000 +#endif + +#define PM_24_GHZ_CHANNEL_6 (6) +#define PM_5_GHZ_CHANNEL_36 (36) +#define CHANNEL_SWITCH_COMPLETE_TIMEOUT (2000) + +/** + * Policy Mgr hardware mode list bit-mask definitions. + * Bits 4:0, 31:29 are unused. + * + * The below definitions are added corresponding to WMI DBS HW mode + * list to make it independent of firmware changes for WMI definitions. + * Currently these definitions have dependency with BIT positions of + * the existing WMI macros. Thus, if the BIT positions are changed for + * WMI macros, then these macros' BIT definitions are also need to be + * changed. + */ +#define POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_BITPOS (28) +#define POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_BITPOS (24) +#define POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_BITPOS (20) +#define POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_BITPOS (16) +#define POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_BITPOS (12) +#define POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_BITPOS (8) +#define POLICY_MGR_HW_MODE_DBS_MODE_BITPOS (7) +#define POLICY_MGR_HW_MODE_AGILE_DFS_MODE_BITPOS (6) +#define POLICY_MGR_HW_MODE_SBS_MODE_BITPOS (5) +#define POLICY_MGR_HW_MODE_MAC0_BAND_BITPOS (3) +#define POLICY_MGR_HW_MODE_ID_BITPOS (0) + +#define POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_MASK \ + (0xf << POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_BITPOS) +#define POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_MASK \ + (0xf << POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_BITPOS) +#define POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_MASK \ + (0xf << POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_BITPOS) +#define POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_MASK \ + (0xf << POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_BITPOS) +#define POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_MASK \ + (0xf << POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_BITPOS) +#define POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_MASK \ + (0xf << POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_BITPOS) +#define POLICY_MGR_HW_MODE_DBS_MODE_MASK \ + (0x1 << POLICY_MGR_HW_MODE_DBS_MODE_BITPOS) +#define POLICY_MGR_HW_MODE_AGILE_DFS_MODE_MASK \ + (0x1 << POLICY_MGR_HW_MODE_AGILE_DFS_MODE_BITPOS) +#define POLICY_MGR_HW_MODE_SBS_MODE_MASK \ + (0x1 << POLICY_MGR_HW_MODE_SBS_MODE_BITPOS) +#define POLICY_MGR_HW_MODE_MAC0_BAND_MASK \ + (0x3 << POLICY_MGR_HW_MODE_MAC0_BAND_BITPOS) +#define POLICY_MGR_HW_MODE_ID_MASK \ + (0x7 << POLICY_MGR_HW_MODE_ID_BITPOS) + +#define POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_BITPOS,\ + 4, value) +#define POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_BITPOS,\ + 4, value) +#define POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_BITPOS,\ + 4, value) +#define POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_BITPOS,\ + 4, value) +#define POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_BITPOS,\ + 4, value) +#define POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_BITPOS,\ + 4, value) +#define POLICY_MGR_HW_MODE_DBS_MODE_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_DBS_MODE_BITPOS,\ + 1, value) +#define POLICY_MGR_HW_MODE_AGILE_DFS_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_AGILE_DFS_MODE_BITPOS,\ + 1, value) +#define POLICY_MGR_HW_MODE_SBS_MODE_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_SBS_MODE_BITPOS,\ + 1, value) +#define POLICY_MGR_HW_MODE_MAC0_BAND_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_MAC0_BAND_BITPOS,\ + 2, value) +#define POLICY_MGR_HW_MODE_ID_SET(hw_mode, value) \ + WMI_SET_BITS(hw_mode, POLICY_MGR_HW_MODE_ID_BITPOS,\ + 3, value) + +#define POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_MASK) >> \ + POLICY_MGR_HW_MODE_MAC0_TX_STREAMS_BITPOS) +#define POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_MASK) >> \ + POLICY_MGR_HW_MODE_MAC0_RX_STREAMS_BITPOS) +#define POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_MASK) >> \ + POLICY_MGR_HW_MODE_MAC1_TX_STREAMS_BITPOS) +#define POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_MASK) >> \ + POLICY_MGR_HW_MODE_MAC1_RX_STREAMS_BITPOS) +#define POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_MASK) >> \ + POLICY_MGR_HW_MODE_MAC0_BANDWIDTH_BITPOS) +#define POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_MASK) >> \ + POLICY_MGR_HW_MODE_MAC1_BANDWIDTH_BITPOS) +#define POLICY_MGR_HW_MODE_DBS_MODE_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_DBS_MODE_MASK) >> \ + POLICY_MGR_HW_MODE_DBS_MODE_BITPOS) +#define POLICY_MGR_HW_MODE_AGILE_DFS_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_AGILE_DFS_MODE_MASK) >> \ + POLICY_MGR_HW_MODE_AGILE_DFS_MODE_BITPOS) +#define POLICY_MGR_HW_MODE_SBS_MODE_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_SBS_MODE_MASK) >> \ + POLICY_MGR_HW_MODE_SBS_MODE_BITPOS) +#define POLICY_MGR_HW_MODE_MAC0_BAND_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_MAC0_BAND_MASK) >> \ + POLICY_MGR_HW_MODE_MAC0_BAND_BITPOS) +#define POLICY_MGR_HW_MODE_ID_GET(hw_mode) \ + (((hw_mode) & POLICY_MGR_HW_MODE_ID_MASK) >> \ + POLICY_MGR_HW_MODE_ID_BITPOS) + +#define POLICY_MGR_DEFAULT_HW_MODE_INDEX 0xFFFF + +#define policy_mgr_alert(params...) \ + QDF_TRACE_FATAL(QDF_MODULE_ID_POLICY_MGR, params) +#define policy_mgr_err(params...) \ + QDF_TRACE_ERROR(QDF_MODULE_ID_POLICY_MGR, params) +#define policy_mgr_warn(params...) \ + QDF_TRACE_WARN(QDF_MODULE_ID_POLICY_MGR, params) +#define policy_mgr_notice(params...) \ + QDF_TRACE_INFO(QDF_MODULE_ID_POLICY_MGR, params) +#define policy_mgr_info(params...) \ + QDF_TRACE_INFO(QDF_MODULE_ID_POLICY_MGR, params) +#define policy_mgr_debug(params...) \ + QDF_TRACE_DEBUG(QDF_MODULE_ID_POLICY_MGR, params) + +#define policymgr_nofl_alert(params...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_POLICY_MGR, params) +#define policymgr_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_POLICY_MGR, params) +#define policymgr_nofl_warn(params...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_POLICY_MGR, params) +#define policymgr_nofl_info(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_POLICY_MGR, params) +#define policymgr_nofl_debug(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_POLICY_MGR, params) + +#define PM_CONC_CONNECTION_LIST_VALID_INDEX(index) \ + ((MAX_NUMBER_OF_CONC_CONNECTIONS > index) && \ + (pm_conc_connection_list[index].in_use)) + +extern struct policy_mgr_conc_connection_info + pm_conc_connection_list[MAX_NUMBER_OF_CONC_CONNECTIONS]; + +extern const enum policy_mgr_pcl_type + first_connection_pcl_table[PM_MAX_NUM_OF_MODE] + [PM_MAX_CONC_PRIORITY_MODE]; +extern pm_dbs_pcl_second_connection_table_type + *second_connection_pcl_dbs_table; +extern pm_dbs_pcl_third_connection_table_type + *third_connection_pcl_dbs_table; +extern policy_mgr_next_action_two_connection_table_type + *next_action_two_connection_table; +extern policy_mgr_next_action_three_connection_table_type + *next_action_three_connection_table; +extern policy_mgr_next_action_two_connection_table_type + *next_action_two_connection_2x2_2g_1x1_5g_table; +extern policy_mgr_next_action_three_connection_table_type + *next_action_three_connection_2x2_2g_1x1_5g_table; + +extern enum policy_mgr_conc_next_action + (*policy_mgr_get_current_pref_hw_mode_ptr) + (struct wlan_objmgr_psoc *psoc); + +/** + * struct sta_ap_intf_check_work_ctx - sta_ap_intf_check_work + * related info + * @psoc: pointer to PSOC object information + */ +struct sta_ap_intf_check_work_ctx { + struct wlan_objmgr_psoc *psoc; +}; + +/** + * struct policy_mgr_cfg - all the policy manager owned configs + * @mcc_to_scc_switch: switch to indicate MCC to SCC config + * @sys_pref: system's preference while selecting PCLs + * @max_conc_cxns: Max allowed concurrenct active connections + * @conc_rule1: concurrency rule1 + * @conc_rule2: concurrency rule2 + * @dbs_selection_plcy: DBS selection policy for concurrency + * @vdev_priority_list: Priority list for various vdevs + * @chnl_select_plcy: Channel selection policy + * @enable_mcc_adaptive_sch: Enable/Disable MCC adaptive scheduler + * @enable_sta_cxn_5g_band: Enable/Disable STA connection in 5G band + */ +struct policy_mgr_cfg { + uint8_t mcc_to_scc_switch; + uint8_t sys_pref; + uint8_t max_conc_cxns; + uint8_t conc_rule1; + uint8_t conc_rule2; + uint8_t enable_mcc_adaptive_sch; + uint8_t enable_sta_cxn_5g_band; + uint32_t dbs_selection_plcy; + uint32_t vdev_priority_list; + uint32_t chnl_select_plcy; +}; + +/** + * struct policy_mgr_psoc_priv_obj - Policy manager private data + * @psoc: pointer to PSOC object information + * @pdev: pointer to PDEV object information + * @connection_update_done_evt: qdf event to synchronize + * connection activities + * @qdf_conc_list_lock: To protect connection table + * @dbs_opportunistic_timer: Timer to drop down to Single Mac + * Mode opportunistically + * @sap_restart_chan_switch_cb: Callback for channel switch + * notification for SAP + * @sme_cbacks: callbacks to be registered by SME for + * interaction with Policy Manager + * @wma_cbacks: callbacks to be registered by SME for + * interaction with Policy Manager + * @tdls_cbacks: callbacks to be registered by SME for + * interaction with Policy Manager + * @cdp_cbacks: callbacks to be registered by SME for + * interaction with Policy Manager + * @sap_mandatory_channels: The user preferred master list on + * which SAP can be brought up. This + * mandatory channel list would be as per + * OEMs preference & conforming to the + * regulatory/other considerations + * @sap_mandatory_channels_len: Length of the SAP mandatory + * channel list + * @do_hw_mode_change: Flag to check if HW mode change is needed + * after vdev is up. Especially used after + * channel switch related vdev restart + * @concurrency_mode: active concurrency combination + * @no_of_open_sessions: Number of active vdevs + * @no_of_active_sessions: Number of active connections + * @sta_ap_intf_check_work: delayed sap restart work + * @num_dbs_hw_modes: Number of different HW modes supported + * @hw_mode: List of HW modes supported + * @old_hw_mode_index: Old HW mode from hw_mode table + * @new_hw_mode_index: New HW mode from hw_mode table + * @dual_mac_cfg: DBS configuration currenctly used by FW for + * scan & connections + * @hw_mode_change_in_progress: This is to track if HW mode + * change is in progress + * @enable_mcc_adaptive_scheduler: Enable MCC adaptive scheduler + * value from INI + * @unsafe_channel_list: LTE coex channel avoidance list + * @unsafe_channel_count: LTE coex channel avoidance list count + * @sta_ap_intf_check_work_info: Info related to sta_ap_intf_check_work + * @opportunistic_update_done_evt: qdf event to synchronize host + * & FW HW mode + */ +struct policy_mgr_psoc_priv_obj { + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_pdev *pdev; + qdf_event_t connection_update_done_evt; + qdf_mutex_t qdf_conc_list_lock; + qdf_mc_timer_t dbs_opportunistic_timer; + struct policy_mgr_hdd_cbacks hdd_cbacks; + struct policy_mgr_sme_cbacks sme_cbacks; + struct policy_mgr_wma_cbacks wma_cbacks; + struct policy_mgr_tdls_cbacks tdls_cbacks; + struct policy_mgr_cdp_cbacks cdp_cbacks; + struct policy_mgr_dp_cbacks dp_cbacks; + bool enable_sap_mandatory_chan_list; + uint8_t sap_mandatory_channels[QDF_MAX_NUM_CHAN]; + uint32_t sap_mandatory_channels_len; + bool do_hw_mode_change; + uint32_t concurrency_mode; + uint8_t no_of_open_sessions[QDF_MAX_NO_OF_MODE]; + uint8_t no_of_active_sessions[QDF_MAX_NO_OF_MODE]; + qdf_work_t sta_ap_intf_check_work; + uint32_t num_dbs_hw_modes; + struct dbs_hw_mode_info hw_mode; + uint32_t old_hw_mode_index; + uint32_t new_hw_mode_index; + struct dual_mac_config dual_mac_cfg; + uint32_t hw_mode_change_in_progress; + struct policy_mgr_user_cfg user_cfg; + uint16_t unsafe_channel_list[QDF_MAX_NUM_CHAN]; + uint16_t unsafe_channel_count; + struct sta_ap_intf_check_work_ctx *sta_ap_intf_check_work_info; + uint8_t cur_conc_system_pref; + uint8_t sta_sap_scc_on_dfs_chan_allowed; + qdf_event_t opportunistic_update_done_evt; + qdf_event_t channel_switch_complete_evt; + send_mode_change_event_cb mode_change_cb; + uint32_t user_config_sap_channel; + struct policy_mgr_cfg cfg; +}; + +/** + * struct policy_mgr_mac_ss_bw_info - hw_mode_list PHY/MAC params for each MAC + * @mac_tx_stream: Max TX stream number supported on MAC + * @mac_rx_stream: Max RX stream number supported on MAC + * @mac_bw: Max bandwidth(wmi_channel_width enum type) + * @mac_band_cap: supported Band bit map(WLAN_2G_CAPABILITY = 0x1, + * WLAN_5G_CAPABILITY = 0x2) + */ +struct policy_mgr_mac_ss_bw_info { + uint32_t mac_tx_stream; + uint32_t mac_rx_stream; + uint32_t mac_bw; + uint32_t mac_band_cap; +}; + +struct policy_mgr_psoc_priv_obj *policy_mgr_get_context( + struct wlan_objmgr_psoc *psoc); +QDF_STATUS policy_mgr_get_updated_scan_config( + struct wlan_objmgr_psoc *psoc, + uint32_t *scan_config, + bool dbs_scan, + bool dbs_plus_agile_scan, + bool single_mac_scan_with_dfs); +QDF_STATUS policy_mgr_get_updated_fw_mode_config( + struct wlan_objmgr_psoc *psoc, + uint32_t *fw_mode_config, + bool dbs, + bool agile_dfs); +bool policy_mgr_is_dual_mac_disabled_in_ini( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_mcc_to_scc_switch_mode() - MCC to SCC + * switch mode value in the user config + * @psoc: PSOC object information + * + * MCC to SCC switch mode value in user config + * + * Return: MCC to SCC switch mode value + */ +uint32_t policy_mgr_get_mcc_to_scc_switch_mode( + struct wlan_objmgr_psoc *psoc); +bool policy_mgr_get_dbs_config(struct wlan_objmgr_psoc *psoc); +bool policy_mgr_get_agile_dfs_config(struct wlan_objmgr_psoc *psoc); +bool policy_mgr_get_dbs_scan_config(struct wlan_objmgr_psoc *psoc); +void policy_mgr_get_tx_rx_ss_from_config(enum hw_mode_ss_config mac_ss, + uint32_t *tx_ss, uint32_t *rx_ss); +int8_t policy_mgr_get_matching_hw_mode_index( + struct wlan_objmgr_psoc *psoc, + uint32_t mac0_tx_ss, uint32_t mac0_rx_ss, + enum hw_mode_bandwidth mac0_bw, + uint32_t mac1_tx_ss, uint32_t mac1_rx_ss, + enum hw_mode_bandwidth mac1_bw, + enum hw_mode_mac_band_cap mac0_band_cap, + enum hw_mode_dbs_capab dbs, + enum hw_mode_agile_dfs_capab dfs, + enum hw_mode_sbs_capab sbs); +int8_t policy_mgr_get_hw_mode_idx_from_dbs_hw_list( + struct wlan_objmgr_psoc *psoc, + enum hw_mode_ss_config mac0_ss, + enum hw_mode_bandwidth mac0_bw, + enum hw_mode_ss_config mac1_ss, + enum hw_mode_bandwidth mac1_bw, + enum hw_mode_mac_band_cap mac0_band_cap, + enum hw_mode_dbs_capab dbs, + enum hw_mode_agile_dfs_capab dfs, + enum hw_mode_sbs_capab sbs); +QDF_STATUS policy_mgr_get_old_and_new_hw_index( + struct wlan_objmgr_psoc *psoc, + uint32_t *old_hw_mode_index, + uint32_t *new_hw_mode_index); +void policy_mgr_update_conc_list(struct wlan_objmgr_psoc *psoc, + uint32_t conn_index, + enum policy_mgr_con_mode mode, + uint8_t chan, + enum hw_mode_bandwidth bw, + uint8_t mac, + enum policy_mgr_chain_mode chain_mask, + uint32_t original_nss, + uint32_t vdev_id, + bool in_use, + bool update_conn); +void policy_mgr_store_and_del_conn_info(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + bool all_matching_cxn_to_del, + struct policy_mgr_conc_connection_info *info, + uint8_t *num_cxn_del); + +/** + * policy_mgr_store_and_del_conn_info_by_vdev_id() - Store and del a + * connection info by vdev id + * @psoc: PSOC object information + * @vdev_id: vdev id whose entry has to be deleted + * @info: struture array pointer where the connection info will be saved + * @num_cxn_del: number of connection which are going to be deleted + * + * Saves the connection info corresponding to the provided mode + * and deleted that corresponding entry based on vdev from the + * connection info structure + * + * Return: None + */ +void policy_mgr_store_and_del_conn_info_by_vdev_id( + struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, + struct policy_mgr_conc_connection_info *info, + uint8_t *num_cxn_del); + +void policy_mgr_restore_deleted_conn_info(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_conc_connection_info *info, + uint8_t num_cxn_del); +void policy_mgr_update_hw_mode_conn_info(struct wlan_objmgr_psoc *psoc, + uint32_t num_vdev_mac_entries, + struct policy_mgr_vdev_mac_map *vdev_mac_map, + struct policy_mgr_hw_mode_params hw_mode); +void policy_mgr_pdev_set_hw_mode_cb(uint32_t status, + uint32_t cfgd_hw_mode_index, + uint32_t num_vdev_mac_entries, + struct policy_mgr_vdev_mac_map *vdev_mac_map, + uint8_t next_action, + enum policy_mgr_conn_update_reason reason, + uint32_t session_id, void *context); +void policy_mgr_dump_current_concurrency(struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_pdev_set_pcl() - SET PCL channel list and send to firmware + * @psoc: PSOC object information + * @mode: Adapter mode + * + * Return: None + */ +void policy_mgr_pdev_set_pcl(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode); +void pm_dbs_opportunistic_timer_handler(void *data); +enum policy_mgr_con_mode policy_mgr_get_mode(uint8_t type, + uint8_t subtype); +enum hw_mode_bandwidth policy_mgr_get_bw(enum phy_ch_width chan_width); +QDF_STATUS policy_mgr_get_channel_list(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_pcl_type pcl, + uint8_t *pcl_channels, uint32_t *len, + enum policy_mgr_con_mode mode, + uint8_t *pcl_weights, uint32_t weight_len); +bool policy_mgr_allow_new_home_channel(struct wlan_objmgr_psoc *psoc, + uint8_t channel, uint32_t num_connections); +bool policy_mgr_is_5g_channel_allowed(struct wlan_objmgr_psoc *psoc, + uint8_t channel, uint32_t *list, + enum policy_mgr_con_mode mode); +QDF_STATUS policy_mgr_complete_action(struct wlan_objmgr_psoc *psoc, + uint8_t new_nss, uint8_t next_action, + enum policy_mgr_conn_update_reason reason, + uint32_t session_id); +enum policy_mgr_con_mode policy_mgr_get_mode_by_vdev_id( + struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); +QDF_STATUS policy_mgr_init_connection_update( + struct policy_mgr_psoc_priv_obj *pm_ctx); +enum policy_mgr_conc_next_action + policy_mgr_get_current_pref_hw_mode_dbs_2x2( + struct wlan_objmgr_psoc *psoc); +enum policy_mgr_conc_next_action + policy_mgr_get_current_pref_hw_mode_dbs_1x1( + struct wlan_objmgr_psoc *psoc); + +/** + * policy_mgr_get_current_pref_hw_mode_dual_dbs() - Get the + * current preferred hw mode + * + * Get the preferred hw mode based on the current connection combinations + * + * Return: No change (PM_NOP), (PM_SINGLE_MAC_UPGRADE), + * DBS (PM_DBS1_DOWNGRADE or PM_DBS2_DOWNGRADE) + */ +enum policy_mgr_conc_next_action + policy_mgr_get_current_pref_hw_mode_dual_dbs( + struct wlan_objmgr_psoc *psoc); + +QDF_STATUS policy_mgr_reset_sap_mandatory_channels( + struct policy_mgr_psoc_priv_obj *pm_ctx); + +/** + * policy_mgr_get_mode_specific_conn_info() - Get active mode specific + * channel and vdev id + * @psoc: PSOC object information + * @channel: Mode specific channel (list) + * @vdev_id: Mode specific vdev id (list) + * @mode: Connection Mode + * + * Get active mode specific channel and vdev id + * + * Return: number of connection found as per given mode + */ +uint32_t policy_mgr_get_mode_specific_conn_info(struct wlan_objmgr_psoc *psoc, + uint8_t *channel, uint8_t *vdev_id, + enum policy_mgr_con_mode mode); + +/** + * policy_mgr_reg_chan_change_callback() - Callback to be + * invoked by regulatory module when valid channel list changes + * @psoc: PSOC object information + * @pdev: PDEV object information + * @chan_list: New channel list + * @avoid_freq_ind: LTE coex avoid channel list + * @arg: Information passed at registration + * + * Get updated channel list from regulatory module + * + * Return: None + */ +void policy_mgr_reg_chan_change_callback(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_pdev *pdev, + struct regulatory_channel *chan_list, + struct avoid_freq_ind_data *avoid_freq_ind, + void *arg); + +/** + * policy_mgr_nss_update() - update nss for AP vdev + * @psoc: PSOC object information + * @new_nss: new NSS value + * @next_action: Next action after nss update + * @band: update AP vdev on the Band. + * @reason: action reason + * @original_vdev_id: original request hwmode change vdev id + * + * The function will update AP vdevs on specific band. + * eg. band = POLICY_MGR_ANY will request to update all band (2g and 5g) + * + * Return: QDF_STATUS_SUCCESS, update requested successfully. + */ +QDF_STATUS policy_mgr_nss_update(struct wlan_objmgr_psoc *psoc, + uint8_t new_nss, uint8_t next_action, + enum policy_mgr_band band, + enum policy_mgr_conn_update_reason reason, + uint32_t original_vdev_id); + +/** + * policy_mgr_is_concurrency_allowed() - Check for allowed + * concurrency combination + * @psoc: PSOC object information + * @mode: new connection mode + * @channel: channel on which new connection is coming up + * @bw: Bandwidth requested by the connection (optional) + * + * When a new connection is about to come up check if current + * concurrency combination including the new connection is + * allowed or not based on the HW capability, but no need to + * invoke get_pcl + * + * Return: True/False + */ +bool policy_mgr_is_concurrency_allowed(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint8_t channel, + enum hw_mode_bandwidth bw); +#endif diff --git a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_init_deinit.c b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_init_deinit.c new file mode 100644 index 000000000000..3b99c7ce6620 --- /dev/null +++ b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_init_deinit.c @@ -0,0 +1,742 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_policy_mgr_init_deinit.c + * + * WLAN Concurrenct Connection Management APIs + * + */ + +/* Include files */ + +#include "wlan_policy_mgr_api.h" +#include "wlan_policy_mgr_tables_1x1_dbs_i.h" +#include "wlan_policy_mgr_tables_2x2_dbs_i.h" +#include "wlan_policy_mgr_tables_2x2_5g_1x1_2g.h" +#include "wlan_policy_mgr_tables_2x2_2g_1x1_5g.h" +#include "wlan_policy_mgr_i.h" +#include "qdf_types.h" +#include "qdf_trace.h" +#include "wlan_objmgr_global_obj.h" + +static QDF_STATUS policy_mgr_psoc_obj_create_cb(struct wlan_objmgr_psoc *psoc, + void *data) +{ + struct policy_mgr_psoc_priv_obj *policy_mgr_ctx; + + policy_mgr_ctx = qdf_mem_malloc( + sizeof(struct policy_mgr_psoc_priv_obj)); + if (!policy_mgr_ctx) { + policy_mgr_err("memory allocation failed"); + return QDF_STATUS_E_FAILURE; + } + + policy_mgr_ctx->psoc = psoc; + policy_mgr_ctx->old_hw_mode_index = POLICY_MGR_DEFAULT_HW_MODE_INDEX; + policy_mgr_ctx->new_hw_mode_index = POLICY_MGR_DEFAULT_HW_MODE_INDEX; + + wlan_objmgr_psoc_component_obj_attach(psoc, + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_ctx, + QDF_STATUS_SUCCESS); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS policy_mgr_psoc_obj_destroy_cb(struct wlan_objmgr_psoc *psoc, + void *data) +{ + struct policy_mgr_psoc_priv_obj *policy_mgr_ctx; + + policy_mgr_ctx = policy_mgr_get_context(psoc); + wlan_objmgr_psoc_component_obj_detach(psoc, + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_ctx); + qdf_mem_free(policy_mgr_ctx); + + return QDF_STATUS_SUCCESS; +} + +static void policy_mgr_psoc_obj_status_cb(struct wlan_objmgr_psoc *psoc, + void *data, QDF_STATUS status) +{ + return; +} + +static QDF_STATUS policy_mgr_pdev_obj_create_cb(struct wlan_objmgr_pdev *pdev, + void *data) +{ + struct policy_mgr_psoc_priv_obj *policy_mgr_ctx; + struct wlan_objmgr_psoc *psoc; + + psoc = wlan_pdev_get_psoc(pdev); + policy_mgr_ctx = policy_mgr_get_context(psoc); + if (!policy_mgr_ctx) { + policy_mgr_err("invalid context"); + return QDF_STATUS_E_FAILURE; + } + + policy_mgr_ctx->pdev = pdev; + + wlan_reg_register_chan_change_callback(psoc, + policy_mgr_reg_chan_change_callback, NULL); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS policy_mgr_pdev_obj_destroy_cb(struct wlan_objmgr_pdev *pdev, + void *data) +{ + struct policy_mgr_psoc_priv_obj *policy_mgr_ctx; + struct wlan_objmgr_psoc *psoc; + + psoc = wlan_pdev_get_psoc(pdev); + policy_mgr_ctx = policy_mgr_get_context(psoc); + if (!policy_mgr_ctx) { + policy_mgr_err("invalid context"); + return QDF_STATUS_E_FAILURE; + } + + policy_mgr_ctx->pdev = NULL; + wlan_reg_unregister_chan_change_callback(psoc, + policy_mgr_reg_chan_change_callback); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS policy_mgr_vdev_obj_create_cb(struct wlan_objmgr_vdev *vdev, + void *data) +{ + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS policy_mgr_vdev_obj_destroy_cb(struct wlan_objmgr_vdev *vdev, + void *data) +{ + return QDF_STATUS_SUCCESS; +} + +static void policy_mgr_vdev_obj_status_cb(struct wlan_objmgr_vdev *vdev, + void *data, QDF_STATUS status) +{ + return; +} + +QDF_STATUS policy_mgr_init(void) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + status = wlan_objmgr_register_psoc_create_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_psoc_obj_create_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to register psoc obj create cback"); + goto err_psoc_create; + } + + status = wlan_objmgr_register_psoc_destroy_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_psoc_obj_destroy_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to register psoc obj delete cback"); + goto err_psoc_delete; + } + + status = wlan_objmgr_register_psoc_status_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_psoc_obj_status_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to register psoc obj status cback"); + goto err_psoc_status; + } + + status = wlan_objmgr_register_pdev_create_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_pdev_obj_create_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to register pdev obj create cback"); + goto err_pdev_create; + } + + status = wlan_objmgr_register_pdev_destroy_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_pdev_obj_destroy_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to register pdev obj delete cback"); + goto err_pdev_delete; + } + + status = wlan_objmgr_register_vdev_create_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_vdev_obj_create_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to register vdev obj create cback"); + goto err_vdev_create; + } + + status = wlan_objmgr_register_vdev_destroy_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_vdev_obj_destroy_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to register vdev obj delete cback"); + goto err_vdev_delete; + } + + status = wlan_objmgr_register_vdev_status_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_vdev_obj_status_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("Failed to register vdev obj status cback"); + goto err_vdev_status; + } + + policy_mgr_notice("Callbacks registered with obj mgr"); + + return QDF_STATUS_SUCCESS; + +err_vdev_status: + wlan_objmgr_unregister_vdev_destroy_handler(WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_vdev_obj_destroy_cb, + NULL); +err_vdev_delete: + wlan_objmgr_unregister_vdev_create_handler(WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_vdev_obj_create_cb, + NULL); +err_vdev_create: + wlan_objmgr_unregister_pdev_destroy_handler(WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_pdev_obj_destroy_cb, + NULL); +err_pdev_delete: + wlan_objmgr_unregister_pdev_create_handler(WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_pdev_obj_create_cb, + NULL); +err_pdev_create: + wlan_objmgr_unregister_psoc_status_handler(WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_psoc_obj_status_cb, + NULL); +err_psoc_status: + wlan_objmgr_unregister_psoc_destroy_handler(WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_psoc_obj_destroy_cb, + NULL); +err_psoc_delete: + wlan_objmgr_unregister_psoc_create_handler(WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_psoc_obj_create_cb, + NULL); +err_psoc_create: + return status; +} + +QDF_STATUS policy_mgr_deinit(void) +{ + QDF_STATUS status; + + status = wlan_objmgr_unregister_psoc_status_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_psoc_obj_status_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Failed to deregister psoc obj status cback"); + + status = wlan_objmgr_unregister_psoc_destroy_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_psoc_obj_destroy_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Failed to deregister psoc obj delete cback"); + + status = wlan_objmgr_unregister_psoc_create_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_psoc_obj_create_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Failed to deregister psoc obj create cback"); + + status = wlan_objmgr_unregister_pdev_destroy_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_pdev_obj_destroy_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Failed to deregister pdev obj delete cback"); + + status = wlan_objmgr_unregister_pdev_create_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_pdev_obj_create_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Failed to deregister pdev obj create cback"); + + status = wlan_objmgr_unregister_vdev_status_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_vdev_obj_status_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Failed to deregister vdev obj status cback"); + + status = wlan_objmgr_unregister_vdev_destroy_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_vdev_obj_destroy_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Failed to deregister vdev obj delete cback"); + + status = wlan_objmgr_unregister_vdev_create_handler( + WLAN_UMAC_COMP_POLICY_MGR, + policy_mgr_vdev_obj_create_cb, + NULL); + if (status != QDF_STATUS_SUCCESS) + policy_mgr_err("Failed to deregister vdev obj create cback"); + + policy_mgr_info("deregistered callbacks with obj mgr successfully"); + + return status; +} + +QDF_STATUS policy_mgr_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + if (!QDF_IS_STATUS_SUCCESS(qdf_mutex_create( + &pm_ctx->qdf_conc_list_lock))) { + policy_mgr_err("Failed to init qdf_conc_list_lock"); + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_psoc_close(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + if (!QDF_IS_STATUS_SUCCESS(qdf_mutex_destroy( + &pm_ctx->qdf_conc_list_lock))) { + policy_mgr_err("Failed to destroy qdf_conc_list_lock"); + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + + if (pm_ctx->hw_mode.hw_mode_list) { + qdf_mem_free(pm_ctx->hw_mode.hw_mode_list); + pm_ctx->hw_mode.hw_mode_list = NULL; + policy_mgr_info("HW list is freed"); + } + + if (pm_ctx->sta_ap_intf_check_work_info) { + qdf_cancel_work(&pm_ctx->sta_ap_intf_check_work); + qdf_mem_free(pm_ctx->sta_ap_intf_check_work_info); + pm_ctx->sta_ap_intf_check_work_info = NULL; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_psoc_enable(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + policy_mgr_debug("Initializing the policy manager"); + + /* init pm_conc_connection_list */ + qdf_mem_zero(pm_conc_connection_list, sizeof(pm_conc_connection_list)); + + /* init dbs_opportunistic_timer */ + status = qdf_mc_timer_init(&pm_ctx->dbs_opportunistic_timer, + QDF_TIMER_TYPE_SW, + pm_dbs_opportunistic_timer_handler, + (void *)psoc); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("Failed to init DBS opportunistic timer"); + return status; + } + + /* init connection_update_done_evt */ + status = policy_mgr_init_connection_update(pm_ctx); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("connection_update_done_evt init failed"); + return status; + } + + status = qdf_event_create(&pm_ctx->opportunistic_update_done_evt); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("opportunistic_update_done_evt init failed"); + return status; + } + + status = qdf_event_create(&pm_ctx->channel_switch_complete_evt); + if (!QDF_IS_STATUS_SUCCESS(status)) { + policy_mgr_err("channel_switch_complete_evt init failed"); + return status; + } + pm_ctx->do_hw_mode_change = false; + pm_ctx->hw_mode_change_in_progress = POLICY_MGR_HW_MODE_NOT_IN_PROGRESS; + /* reset sap mandatory channels */ + status = policy_mgr_reset_sap_mandatory_channels(pm_ctx); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to reset mandatory channels"); + return status; + } + + /* init PCL table & function pointers based on HW capability */ + if (policy_mgr_is_hw_dbs_2x2_capable(psoc)) + policy_mgr_get_current_pref_hw_mode_ptr = + policy_mgr_get_current_pref_hw_mode_dbs_2x2; + else if (policy_mgr_is_2x2_1x1_dbs_capable(psoc)) + policy_mgr_get_current_pref_hw_mode_ptr = + policy_mgr_get_current_pref_hw_mode_dual_dbs; + else + policy_mgr_get_current_pref_hw_mode_ptr = + policy_mgr_get_current_pref_hw_mode_dbs_1x1; + + if (policy_mgr_is_hw_dbs_2x2_capable(psoc) || + policy_mgr_is_2x2_1x1_dbs_capable(psoc)) + second_connection_pcl_dbs_table = + &pm_second_connection_pcl_dbs_2x2_table; + else + second_connection_pcl_dbs_table = + &pm_second_connection_pcl_dbs_1x1_table; + + if (policy_mgr_is_hw_dbs_2x2_capable(psoc) || + policy_mgr_is_2x2_1x1_dbs_capable(psoc)) + third_connection_pcl_dbs_table = + &pm_third_connection_pcl_dbs_2x2_table; + else + third_connection_pcl_dbs_table = + &pm_third_connection_pcl_dbs_1x1_table; + + if (policy_mgr_is_hw_dbs_2x2_capable(psoc)) { + next_action_two_connection_table = + &pm_next_action_two_connection_dbs_2x2_table; + } else if (policy_mgr_is_2x2_1x1_dbs_capable(psoc)) { + next_action_two_connection_table = + &pm_next_action_two_connection_dbs_2x2_5g_1x1_2g_table; + next_action_two_connection_2x2_2g_1x1_5g_table = + &pm_next_action_two_connection_dbs_2x2_2g_1x1_5g_table; + } else { + next_action_two_connection_table = + &pm_next_action_two_connection_dbs_1x1_table; + } + + if (policy_mgr_is_hw_dbs_2x2_capable(psoc)) { + next_action_three_connection_table = + &pm_next_action_three_connection_dbs_2x2_table; + } else if (policy_mgr_is_2x2_1x1_dbs_capable(psoc)) { + next_action_three_connection_table = + &pm_next_action_three_connection_dbs_2x2_5g_1x1_2g_table; + next_action_three_connection_2x2_2g_1x1_5g_table = + &pm_next_action_three_connection_dbs_2x2_2g_1x1_5g_table; + } else { + next_action_three_connection_table = + &pm_next_action_three_connection_dbs_1x1_table; + } + policy_mgr_debug("is DBS Capable %d, is SBS Capable %d", + policy_mgr_is_hw_dbs_capable(psoc), + policy_mgr_is_hw_sbs_capable(psoc)); + policy_mgr_debug("is2x2 %d, is2x2+1x1 %d, is2x2_5g+1x1_2g %d, is2x2_2g+1x1_5g %d", + policy_mgr_is_hw_dbs_2x2_capable(psoc), + policy_mgr_is_2x2_1x1_dbs_capable(psoc), + policy_mgr_is_2x2_5G_1x1_2G_dbs_capable(psoc), + policy_mgr_is_2x2_2G_1x1_5G_dbs_capable(psoc)); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_psoc_disable(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + /* destroy connection_update_done_evt */ + if (!QDF_IS_STATUS_SUCCESS(qdf_event_destroy + (&pm_ctx->connection_update_done_evt))) { + policy_mgr_err("Failed to destroy connection_update_done_evt"); + status = QDF_STATUS_E_FAILURE; + QDF_ASSERT(0); + } + + /* destroy opportunistic_update_done_evt */ + if (!QDF_IS_STATUS_SUCCESS(qdf_event_destroy + (&pm_ctx->opportunistic_update_done_evt))) { + policy_mgr_err("Failed to destroy opportunistic_update_done_evt"); + status = QDF_STATUS_E_FAILURE; + QDF_ASSERT(0); + } + /* destroy channel_switch_complete_evt */ + if (!QDF_IS_STATUS_SUCCESS(qdf_event_destroy + (&pm_ctx->channel_switch_complete_evt))) { + policy_mgr_err("Failed to destroy channel_switch_complete evt"); + status = QDF_STATUS_E_FAILURE; + QDF_ASSERT(0); + } + + /* deallocate dbs_opportunistic_timer */ + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state( + &pm_ctx->dbs_opportunistic_timer)) { + qdf_mc_timer_stop(&pm_ctx->dbs_opportunistic_timer); + } + + if (!QDF_IS_STATUS_SUCCESS(qdf_mc_timer_destroy( + &pm_ctx->dbs_opportunistic_timer))) { + policy_mgr_err("Cannot deallocate dbs opportunistic timer"); + status = QDF_STATUS_E_FAILURE; + QDF_ASSERT(0); + } + + /* reset sap mandatory channels */ + if (QDF_IS_STATUS_ERROR( + policy_mgr_reset_sap_mandatory_channels(pm_ctx))) { + policy_mgr_err("failed to reset sap mandatory channels"); + status = QDF_STATUS_E_FAILURE; + QDF_ASSERT(0); + } + + /* deinit pm_conc_connection_list */ + qdf_mem_zero(pm_conc_connection_list, sizeof(pm_conc_connection_list)); + + return status; +} + +QDF_STATUS policy_mgr_register_sme_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_sme_cbacks *sme_cbacks) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->sme_cbacks.sme_get_nss_for_vdev = + sme_cbacks->sme_get_nss_for_vdev; + pm_ctx->sme_cbacks.sme_get_valid_channels = + sme_cbacks->sme_get_valid_channels; + pm_ctx->sme_cbacks.sme_nss_update_request = + sme_cbacks->sme_nss_update_request; + pm_ctx->sme_cbacks.sme_pdev_set_hw_mode = + sme_cbacks->sme_pdev_set_hw_mode; + pm_ctx->sme_cbacks.sme_pdev_set_pcl = + sme_cbacks->sme_pdev_set_pcl; + pm_ctx->sme_cbacks.sme_soc_set_dual_mac_config = + sme_cbacks->sme_soc_set_dual_mac_config; + pm_ctx->sme_cbacks.sme_change_mcc_beacon_interval = + sme_cbacks->sme_change_mcc_beacon_interval; + pm_ctx->sme_cbacks.sme_get_ap_channel_from_scan = + sme_cbacks->sme_get_ap_channel_from_scan; + pm_ctx->sme_cbacks.sme_scan_result_purge = + sme_cbacks->sme_scan_result_purge; + + return QDF_STATUS_SUCCESS; +} + +/** + * policy_mgr_register_hdd_cb() - register HDD callbacks + * @psoc: PSOC object information + * @hdd_cbacks: function pointers from HDD + * + * API, allows HDD to register callbacks to be invoked by policy + * mgr + * + * Return: SUCCESS, + * Failure (if registration fails) + */ +QDF_STATUS policy_mgr_register_hdd_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_hdd_cbacks *hdd_cbacks) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->hdd_cbacks.sap_restart_chan_switch_cb = + hdd_cbacks->sap_restart_chan_switch_cb; + pm_ctx->hdd_cbacks.wlan_hdd_get_channel_for_sap_restart = + hdd_cbacks->wlan_hdd_get_channel_for_sap_restart; + pm_ctx->hdd_cbacks.get_mode_for_non_connected_vdev = + hdd_cbacks->get_mode_for_non_connected_vdev; + pm_ctx->hdd_cbacks.hdd_get_device_mode = + hdd_cbacks->hdd_get_device_mode; + pm_ctx->hdd_cbacks.hdd_wapi_security_sta_exist = + hdd_cbacks->hdd_wapi_security_sta_exist; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_deregister_hdd_cb(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->hdd_cbacks.sap_restart_chan_switch_cb = NULL; + pm_ctx->hdd_cbacks.wlan_hdd_get_channel_for_sap_restart = NULL; + pm_ctx->hdd_cbacks.get_mode_for_non_connected_vdev = NULL; + pm_ctx->hdd_cbacks.hdd_get_device_mode = NULL; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_register_wma_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_wma_cbacks *wma_cbacks) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->wma_cbacks.wma_get_connection_info = + wma_cbacks->wma_get_connection_info; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_register_cdp_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_cdp_cbacks *cdp_cbacks) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->cdp_cbacks.cdp_update_mac_id = + cdp_cbacks->cdp_update_mac_id; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_register_dp_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_dp_cbacks *dp_cbacks) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->dp_cbacks.hdd_disable_rx_ol_in_concurrency = + dp_cbacks->hdd_disable_rx_ol_in_concurrency; + pm_ctx->dp_cbacks.hdd_set_rx_mode_rps_cb = + dp_cbacks->hdd_set_rx_mode_rps_cb; + pm_ctx->dp_cbacks.hdd_ipa_set_mcc_mode_cb = + dp_cbacks->hdd_ipa_set_mcc_mode_cb; + pm_ctx->dp_cbacks.hdd_v2_flow_pool_map = + dp_cbacks->hdd_v2_flow_pool_map; + pm_ctx->dp_cbacks.hdd_v2_flow_pool_unmap = + dp_cbacks->hdd_v2_flow_pool_unmap; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_register_tdls_cb(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_tdls_cbacks *tdls_cbacks) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->tdls_cbacks.tdls_notify_increment_session = + tdls_cbacks->tdls_notify_increment_session; + pm_ctx->tdls_cbacks.tdls_notify_decrement_session = + tdls_cbacks->tdls_notify_decrement_session; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_register_mode_change_cb(struct wlan_objmgr_psoc *psoc, + send_mode_change_event_cb mode_change_cb) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->mode_change_cb = mode_change_cb; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_deregister_mode_change_cb(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + pm_ctx->mode_change_cb = NULL; + + return QDF_STATUS_SUCCESS; +} diff --git a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_pcl.c b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_pcl.c new file mode 100644 index 000000000000..46d1556c61d3 --- /dev/null +++ b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_pcl.c @@ -0,0 +1,1763 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_policy_mgr_pcl.c + * + * WLAN Concurrenct Connection Management APIs + * + */ + +/* Include files */ + +#include "wlan_policy_mgr_api.h" +#include "wlan_policy_mgr_tables_no_dbs_i.h" +#include "wlan_policy_mgr_i.h" +#include "qdf_types.h" +#include "qdf_trace.h" +#include "wlan_objmgr_global_obj.h" +#include "wlan_utility.h" + +/** + * first_connection_pcl_table - table which provides PCL for the + * very first connection in the system + */ +const enum policy_mgr_pcl_type +first_connection_pcl_table[PM_MAX_NUM_OF_MODE] + [PM_MAX_CONC_PRIORITY_MODE] = { + [PM_STA_MODE] = {PM_NONE, PM_NONE, PM_NONE}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G }, + [PM_P2P_CLIENT_MODE] = {PM_5G, PM_5G, PM_5G }, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G }, + [PM_IBSS_MODE] = {PM_NONE, PM_NONE, PM_NONE}, +}; + +pm_dbs_pcl_second_connection_table_type + *second_connection_pcl_dbs_table; +pm_dbs_pcl_third_connection_table_type + *third_connection_pcl_dbs_table; +policy_mgr_next_action_two_connection_table_type + *next_action_two_connection_table; +policy_mgr_next_action_three_connection_table_type + *next_action_three_connection_table; +policy_mgr_next_action_two_connection_table_type + *next_action_two_connection_2x2_2g_1x1_5g_table; +policy_mgr_next_action_three_connection_table_type + *next_action_three_connection_2x2_2g_1x1_5g_table; + +QDF_STATUS policy_mgr_get_pcl_for_existing_conn(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint8_t *pcl_ch, uint32_t *len, + uint8_t *pcl_weight, uint32_t weight_len, + bool all_matching_cxn_to_del) +{ + struct policy_mgr_conc_connection_info + info[MAX_NUMBER_OF_CONC_CONNECTIONS] = { {0} }; + uint8_t num_cxn_del = 0; + + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + policy_mgr_debug("get pcl for existing conn:%d", mode); + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (policy_mgr_mode_specific_connection_count(psoc, mode, NULL) > 0) { + /* Check, store and temp delete the mode's parameter */ + policy_mgr_store_and_del_conn_info(psoc, mode, + all_matching_cxn_to_del, info, &num_cxn_del); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + /* Get the PCL */ + status = policy_mgr_get_pcl(psoc, mode, pcl_ch, len, + pcl_weight, weight_len); + policy_mgr_debug("Get PCL to FW for mode:%d", mode); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + /* Restore the connection info */ + policy_mgr_restore_deleted_conn_info(psoc, info, num_cxn_del); + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return status; +} + +void policy_mgr_decr_session_set_pcl(struct wlan_objmgr_psoc *psoc, + enum QDF_OPMODE mode, + uint8_t session_id) +{ + QDF_STATUS qdf_status; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + qdf_status = policy_mgr_decr_active_session(psoc, mode, session_id); + if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { + policy_mgr_debug("Invalid active session"); + return; + } + + /* + * After the removal of this connection, we need to check if + * a STA connection still exists. The reason for this is that + * if one or more STA exists, we need to provide the updated + * PCL to the FW for cases like LFR. + * + * Since policy_mgr_get_pcl provides PCL list based on the new + * connection that is going to come up, we will find the + * existing STA entry, save it and delete it temporarily. + * After this we will get PCL as though as new STA connection + * is coming up. This will give the exact PCL that needs to be + * given to the FW. After setting the PCL, we need to restore + * the entry that we have saved before. + */ + policy_mgr_set_pcl_for_existing_combo(psoc, PM_STA_MODE); + /* do we need to change the HW mode */ + policy_mgr_check_n_start_opportunistic_timer(psoc); + return; +} + +void policy_mgr_reg_chan_change_callback(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_pdev *pdev, + struct regulatory_channel *chan_list, + struct avoid_freq_ind_data *avoid_freq_ind, + void *arg) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + if (!avoid_freq_ind) { + policy_mgr_debug("avoid_freq_ind NULL"); + return; + } + + /* + * The ch_list buffer can accomadate a maximum of + * NUM_CHANNELS and hence the ch_cnt should also not + * exceed NUM_CHANNELS. + */ + pm_ctx->unsafe_channel_count = avoid_freq_ind->chan_list.ch_cnt >= + NUM_CHANNELS ? + NUM_CHANNELS : avoid_freq_ind->chan_list.ch_cnt; + if (pm_ctx->unsafe_channel_count) + qdf_mem_copy(pm_ctx->unsafe_channel_list, + avoid_freq_ind->chan_list.ch_list, + pm_ctx->unsafe_channel_count * + sizeof(pm_ctx->unsafe_channel_list[0])); + policy_mgr_debug("Channel list update, received %d avoided channels", + pm_ctx->unsafe_channel_count); +} + +void policy_mgr_update_with_safe_channel_list(struct wlan_objmgr_psoc *psoc, + uint8_t *pcl_channels, uint32_t *len, + uint8_t *weight_list, uint32_t weight_len) +{ + uint8_t current_channel_list[QDF_MAX_NUM_CHAN]; + uint8_t org_weight_list[QDF_MAX_NUM_CHAN]; + uint8_t is_unsafe = 1; + uint8_t i, j; + uint32_t safe_channel_count = 0, current_channel_count = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return; + } + + if (len) { + current_channel_count = QDF_MIN(*len, QDF_MAX_NUM_CHAN); + } else { + policy_mgr_err("invalid number of channel length"); + return; + } + + if (pm_ctx->unsafe_channel_count == 0) { + policy_mgr_debug("There are no unsafe channels"); + return; + } + + qdf_mem_copy(current_channel_list, pcl_channels, + current_channel_count); + qdf_mem_zero(pcl_channels, current_channel_count); + + qdf_mem_copy(org_weight_list, weight_list, QDF_MAX_NUM_CHAN); + qdf_mem_zero(weight_list, weight_len); + + for (i = 0; i < current_channel_count; i++) { + is_unsafe = 0; + for (j = 0; j < pm_ctx->unsafe_channel_count; j++) { + if (current_channel_list[i] == + pm_ctx->unsafe_channel_list[j]) { + /* Found unsafe channel, update it */ + is_unsafe = 1; + policy_mgr_debug("CH %d is not safe", + current_channel_list[i]); + break; + } + } + if (!is_unsafe) { + pcl_channels[safe_channel_count] = + current_channel_list[i]; + if (safe_channel_count < weight_len) + weight_list[safe_channel_count] = + org_weight_list[i]; + safe_channel_count++; + } + } + *len = safe_channel_count; + + return; +} + +static QDF_STATUS policy_mgr_modify_pcl_based_on_enabled_channels( + struct policy_mgr_psoc_priv_obj *pm_ctx, + uint8_t *pcl_list_org, + uint8_t *weight_list_org, + uint32_t *pcl_len_org) +{ + uint32_t i, pcl_len = 0; + + for (i = 0; i < *pcl_len_org; i++) { + if (!wlan_reg_is_passive_or_disable_ch( + pm_ctx->pdev, pcl_list_org[i])) { + pcl_list_org[pcl_len] = pcl_list_org[i]; + weight_list_org[pcl_len++] = weight_list_org[i]; + } + } + *pcl_len_org = pcl_len; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS policy_mgr_modify_pcl_based_on_dnbs( + struct wlan_objmgr_psoc *psoc, + uint8_t *pcl_list_org, + uint8_t *weight_list_org, + uint32_t *pcl_len_org) +{ + uint32_t i, pcl_len = 0; + uint8_t pcl_list[QDF_MAX_NUM_CHAN]; + uint8_t weight_list[QDF_MAX_NUM_CHAN]; + bool ok; + int ret; + + for (i = 0; i < *pcl_len_org; i++) { + ret = policy_mgr_is_chan_ok_for_dnbs(psoc, pcl_list_org[i], + &ok); + + if (QDF_IS_STATUS_ERROR(ret)) { + policy_mgr_err("Not able to check DNBS eligibility"); + return ret; + } + if (ok) { + pcl_list[pcl_len] = pcl_list_org[i]; + weight_list[pcl_len++] = weight_list_org[i]; + } + } + + qdf_mem_zero(pcl_list_org, QDF_ARRAY_SIZE(pcl_list_org)); + qdf_mem_zero(weight_list_org, QDF_ARRAY_SIZE(weight_list_org)); + qdf_mem_copy(pcl_list_org, pcl_list, pcl_len); + qdf_mem_copy(weight_list_org, weight_list, pcl_len); + *pcl_len_org = pcl_len; + + return QDF_STATUS_SUCCESS; +} + +uint8_t policy_mgr_get_channel(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, uint32_t *vdev_id) +{ + uint32_t idx = 0; + uint8_t chan; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return 0; + } + + if (mode >= PM_MAX_NUM_OF_MODE) { + policy_mgr_err("incorrect mode"); + return 0; + } + + for (idx = 0; idx < MAX_NUMBER_OF_CONC_CONNECTIONS; idx++) { + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if ((pm_conc_connection_list[idx].mode == mode) && + (!vdev_id || (*vdev_id == + pm_conc_connection_list[idx].vdev_id)) + && pm_conc_connection_list[idx].in_use) { + chan = pm_conc_connection_list[idx].chan; + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + return chan; + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + } + + return 0; +} + +static QDF_STATUS policy_mgr_modify_sap_pcl_based_on_nol( + struct wlan_objmgr_psoc *psoc, + uint8_t *pcl_list_org, + uint8_t *weight_list_org, + uint32_t *pcl_len_org) +{ + uint32_t i, pcl_len = 0; + uint8_t pcl_list[QDF_MAX_NUM_CHAN]; + uint8_t weight_list[QDF_MAX_NUM_CHAN]; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + for (i = 0; i < *pcl_len_org; i++) { + if (!wlan_reg_is_disable_ch(pm_ctx->pdev, pcl_list_org[i])) { + pcl_list[pcl_len] = pcl_list_org[i]; + weight_list[pcl_len++] = weight_list_org[i]; + } + } + + qdf_mem_zero(pcl_list_org, QDF_ARRAY_SIZE(pcl_list_org)); + qdf_mem_zero(weight_list_org, QDF_ARRAY_SIZE(weight_list_org)); + qdf_mem_copy(pcl_list_org, pcl_list, pcl_len); + qdf_mem_copy(weight_list_org, weight_list, pcl_len); + *pcl_len_org = pcl_len; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +policy_mgr_modify_sap_pcl_based_on_srd(struct wlan_objmgr_psoc *psoc, + uint8_t *pcl_list_org, + uint8_t *weight_list_org, + uint32_t *pcl_len_org) +{ + uint32_t i, pcl_len = 0; + uint8_t pcl_list[QDF_MAX_NUM_CHAN]; + uint8_t weight_list[QDF_MAX_NUM_CHAN]; + struct policy_mgr_psoc_priv_obj *pm_ctx; + bool is_etsi13_srd_chan_allowed_in_mas_mode = true; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + is_etsi13_srd_chan_allowed_in_mas_mode = + wlan_reg_is_etsi13_srd_chan_allowed_master_mode(pm_ctx->pdev); + + if (is_etsi13_srd_chan_allowed_in_mas_mode) + return QDF_STATUS_SUCCESS; + + for (i = 0; i < *pcl_len_org; i++) { + if (wlan_reg_is_etsi13_srd_chan(pm_ctx->pdev, + pcl_list_org[i])) + continue; + pcl_list[pcl_len] = pcl_list_org[i]; + weight_list[pcl_len++] = weight_list_org[i]; + } + + qdf_mem_zero(pcl_list_org, QDF_ARRAY_SIZE(pcl_list_org)); + qdf_mem_zero(weight_list_org, QDF_ARRAY_SIZE(weight_list_org)); + qdf_mem_copy(pcl_list_org, pcl_list, pcl_len); + qdf_mem_copy(weight_list_org, weight_list, pcl_len); + *pcl_len_org = pcl_len; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS policy_mgr_pcl_modification_for_sap( + struct wlan_objmgr_psoc *psoc, + uint8_t *pcl_channels, uint8_t *pcl_weight, + uint32_t *len) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t i; + + if (policy_mgr_is_sap_mandatory_channel_set(psoc)) { + status = policy_mgr_modify_sap_pcl_based_on_mandatory_channel( + psoc, pcl_channels, pcl_weight, len); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get modified pcl for SAP"); + return status; + } + policy_mgr_debug("modified pcl len:%d", *len); + for (i = 0; i < *len; i++) + policy_mgr_debug("chan:%d weight:%d", + pcl_channels[i], pcl_weight[i]); + } + + status = policy_mgr_modify_sap_pcl_based_on_nol( + psoc, pcl_channels, pcl_weight, len); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get modified pcl for SAP"); + return status; + } + policy_mgr_debug("modified pcl len:%d", *len); + for (i = 0; i < *len; i++) + policy_mgr_debug("chan:%d weight:%d", + pcl_channels[i], pcl_weight[i]); + + status = policy_mgr_modify_sap_pcl_based_on_srd + (psoc, pcl_channels, pcl_weight, len); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get modified pcl for SAP"); + return status; + } + policy_mgr_debug("modified final pcl len:%d", *len); + for (i = 0; i < *len; i++) + policy_mgr_debug("chan:%d weight:%d", + pcl_channels[i], pcl_weight[i]); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS policy_mgr_pcl_modification_for_p2p_go( + struct wlan_objmgr_psoc *psoc, + uint8_t *pcl_channels, uint8_t *pcl_weight, + uint32_t *len) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct policy_mgr_psoc_priv_obj *pm_ctx; + uint32_t i; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("context is NULL"); + return status; + } + + status = policy_mgr_modify_pcl_based_on_enabled_channels( + pm_ctx, pcl_channels, pcl_weight, len); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get modified pcl for GO"); + return status; + } + policy_mgr_debug("modified pcl len:%d", *len); + for (i = 0; i < *len; i++) + policy_mgr_debug("chan:%d weight:%d", + pcl_channels[i], pcl_weight[i]); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS policy_mgr_mode_specific_modification_on_pcl( + struct wlan_objmgr_psoc *psoc, + uint8_t *pcl_channels, uint8_t *pcl_weight, + uint32_t *len, enum policy_mgr_con_mode mode) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + switch (mode) { + case PM_SAP_MODE: + status = policy_mgr_pcl_modification_for_sap( + psoc, pcl_channels, pcl_weight, len); + break; + case PM_P2P_GO_MODE: + status = policy_mgr_pcl_modification_for_p2p_go( + psoc, pcl_channels, pcl_weight, len); + break; + case PM_STA_MODE: + case PM_P2P_CLIENT_MODE: + case PM_IBSS_MODE: + status = QDF_STATUS_SUCCESS; + break; + default: + policy_mgr_err("unexpected mode %d", mode); + break; + } + + return status; +} + +QDF_STATUS policy_mgr_get_pcl(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + uint8_t *pcl_channels, uint32_t *len, + uint8_t *pcl_weight, uint32_t weight_len) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint32_t num_connections = 0, i; + enum policy_mgr_conc_priority_mode first_index = 0; + enum policy_mgr_one_connection_mode second_index = 0; + enum policy_mgr_two_connection_mode third_index = 0; + enum policy_mgr_pcl_type pcl = PM_NONE; + enum policy_mgr_conc_priority_mode conc_system_pref = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + enum QDF_OPMODE qdf_mode; + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("context is NULL"); + return status; + } + + if ((mode < 0) || (mode >= PM_MAX_NUM_OF_MODE)) { + policy_mgr_err("Invalid connection mode %d received", mode); + return status; + } + + /* find the current connection state from pm_conc_connection_list*/ + num_connections = policy_mgr_get_connection_count(psoc); + policy_mgr_debug("connections:%d pref:%d requested mode:%d", + num_connections, pm_ctx->cur_conc_system_pref, mode); + + switch (pm_ctx->cur_conc_system_pref) { + case 0: + conc_system_pref = PM_THROUGHPUT; + break; + case 1: + conc_system_pref = PM_POWERSAVE; + break; + case 2: + conc_system_pref = PM_LATENCY; + break; + default: + policy_mgr_err("unknown cur_conc_system_pref value %d", + pm_ctx->cur_conc_system_pref); + break; + } + + switch (num_connections) { + case 0: + first_index = + policy_mgr_get_first_connection_pcl_table_index(psoc); + pcl = first_connection_pcl_table[mode][first_index]; + break; + case 1: + second_index = + policy_mgr_get_second_connection_pcl_table_index(psoc); + if (PM_MAX_ONE_CONNECTION_MODE == second_index) { + policy_mgr_err("couldn't find index for 2nd connection pcl table"); + return status; + } + qdf_mode = policy_mgr_get_qdf_mode_from_pm(mode); + if (qdf_mode == QDF_MAX_NO_OF_MODE) + return status; + + if (policy_mgr_is_hw_dbs_capable(psoc) == true && + policy_mgr_is_dbs_allowed_for_concurrency( + psoc, qdf_mode)) { + pcl = (*second_connection_pcl_dbs_table) + [second_index][mode][conc_system_pref]; + } else { + pcl = second_connection_pcl_nodbs_table + [second_index][mode][conc_system_pref]; + } + + break; + case 2: + third_index = + policy_mgr_get_third_connection_pcl_table_index(psoc); + if (PM_MAX_TWO_CONNECTION_MODE == third_index) { + policy_mgr_err( + "couldn't find index for 3rd connection pcl table"); + return status; + } + if (policy_mgr_is_hw_dbs_capable(psoc) == true) { + pcl = (*third_connection_pcl_dbs_table) + [third_index][mode][conc_system_pref]; + } else { + pcl = third_connection_pcl_nodbs_table + [third_index][mode][conc_system_pref]; + } + break; + default: + policy_mgr_err("unexpected num_connections value %d", + num_connections); + break; + } + + policy_mgr_debug("index1:%d index2:%d index3:%d pcl:%d dbs:%d", + first_index, second_index, third_index, + pcl, policy_mgr_is_hw_dbs_capable(psoc)); + + /* once the PCL enum is obtained find out the exact channel list with + * help from sme_get_cfg_valid_channels + */ + status = policy_mgr_get_channel_list(psoc, pcl, pcl_channels, len, mode, + pcl_weight, weight_len); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get channel list:%d", status); + return status; + } + + policy_mgr_debug("pcl len:%d", *len); + for (i = 0; i < *len; i++) { + policy_mgr_debug("chan:%d weight:%d", + pcl_channels[i], pcl_weight[i]); + } + + policy_mgr_mode_specific_modification_on_pcl( + psoc, pcl_channels, pcl_weight, len, mode); + + status = policy_mgr_modify_pcl_based_on_dnbs(psoc, pcl_channels, + pcl_weight, len); + + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("failed to get modified pcl based on DNBS"); + return status; + } + return QDF_STATUS_SUCCESS; +} + +enum policy_mgr_conc_priority_mode + policy_mgr_get_first_connection_pcl_table_index( + struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("context is NULL"); + return PM_THROUGHPUT; + } + + if (pm_ctx->cur_conc_system_pref >= PM_MAX_CONC_PRIORITY_MODE) + return PM_THROUGHPUT; + + return pm_ctx->cur_conc_system_pref; +} + +enum policy_mgr_one_connection_mode + policy_mgr_get_second_connection_pcl_table_index( + struct wlan_objmgr_psoc *psoc) +{ + enum policy_mgr_one_connection_mode index = PM_MAX_ONE_CONNECTION_MODE; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return index; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (PM_STA_MODE == pm_conc_connection_list[0].mode) { + if (WLAN_REG_IS_24GHZ_CH(pm_conc_connection_list[0].chan)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_24_1x1; + else + index = PM_STA_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_5_1x1; + else + index = PM_STA_5_2x2; + } + } else if (PM_SAP_MODE == pm_conc_connection_list[0].mode) { + if (WLAN_REG_IS_24GHZ_CH(pm_conc_connection_list[0].chan)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_SAP_24_1x1; + else + index = PM_SAP_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_SAP_5_1x1; + else + index = PM_SAP_5_2x2; + } + } else if (PM_P2P_CLIENT_MODE == pm_conc_connection_list[0].mode) { + if (WLAN_REG_IS_24GHZ_CH(pm_conc_connection_list[0].chan)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_CLI_24_1x1; + else + index = PM_P2P_CLI_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_CLI_5_1x1; + else + index = PM_P2P_CLI_5_2x2; + } + } else if (PM_P2P_GO_MODE == pm_conc_connection_list[0].mode) { + if (WLAN_REG_IS_24GHZ_CH(pm_conc_connection_list[0].chan)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_24_1x1; + else + index = PM_P2P_GO_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_5_1x1; + else + index = PM_P2P_GO_5_2x2; + } + } else if (PM_IBSS_MODE == pm_conc_connection_list[0].mode) { + if (WLAN_REG_IS_24GHZ_CH(pm_conc_connection_list[0].chan)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_IBSS_24_1x1; + else + index = PM_IBSS_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_IBSS_5_1x1; + else + index = PM_IBSS_5_2x2; + } + } + + policy_mgr_debug("mode:%d chan:%d chain:%d index:%d", + pm_conc_connection_list[0].mode, + pm_conc_connection_list[0].chan, + pm_conc_connection_list[0].chain_mask, index); + + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_cli_sap(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].chan == + pm_conc_connection_list[1].chan) { + if (WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[0].chan)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_CLI_SAP_SCC_24_1x1; + else + index = PM_P2P_CLI_SAP_SCC_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_CLI_SAP_SCC_5_1x1; + else + index = PM_P2P_CLI_SAP_SCC_5_2x2; + } + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if ((WLAN_REG_IS_24GHZ_CH + (pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_24GHZ_CH + (pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_CLI_SAP_MCC_24_1x1; + else + index = PM_P2P_CLI_SAP_MCC_24_2x2; + } else if ((WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_CLI_SAP_MCC_5_1x1; + else + index = PM_P2P_CLI_SAP_MCC_5_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_CLI_SAP_MCC_24_5_1x1; + else + index = PM_P2P_CLI_SAP_MCC_24_5_2x2; + } + /* SBS or DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + /* SBS */ + if ((WLAN_REG_IS_5GHZ_CH(pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_5GHZ_CH(pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_CLI_SAP_SBS_5_1x1; + } else { + /* DBS */ + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_CLI_SAP_DBS_1x1; + else + index = PM_P2P_CLI_SAP_DBS_2x2; + } + } + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_sta_sap(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].chan == + pm_conc_connection_list[1].chan) { + if (WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[0].chan)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_SAP_SCC_24_1x1; + else + index = PM_STA_SAP_SCC_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_SAP_SCC_5_1x1; + else + index = PM_STA_SAP_SCC_5_2x2; + } + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if ((WLAN_REG_IS_24GHZ_CH + (pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_24GHZ_CH + (pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_SAP_MCC_24_1x1; + else + index = PM_STA_SAP_MCC_24_2x2; + } else if ((WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_SAP_MCC_5_1x1; + else + index = PM_STA_SAP_MCC_5_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_SAP_MCC_24_5_1x1; + else + index = PM_STA_SAP_MCC_24_5_2x2; + } + /* SBS or DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + /* SBS */ + if ((WLAN_REG_IS_5GHZ_CH(pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_5GHZ_CH(pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_SAP_SBS_5_1x1; + } else { + /* DBS */ + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_SAP_DBS_1x1; + else + index = PM_STA_SAP_DBS_2x2; + } + } + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_sap_sap(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].chan == + pm_conc_connection_list[1].chan) { + if (WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[0].chan)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_SAP_SAP_SCC_24_1x1; + else + index = PM_SAP_SAP_SCC_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_SAP_SAP_SCC_5_1x1; + else + index = PM_SAP_SAP_SCC_5_2x2; + } + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if ((WLAN_REG_IS_24GHZ_CH + (pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_24GHZ_CH + (pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_SAP_SAP_MCC_24_1x1; + else + index = PM_SAP_SAP_MCC_24_2x2; + } else if ((WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_SAP_SAP_MCC_5_1x1; + else + index = PM_SAP_SAP_MCC_5_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_SAP_SAP_MCC_24_5_1x1; + else + index = PM_SAP_SAP_MCC_24_5_2x2; + } + /* SBS or DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + /* SBS */ + if ((WLAN_REG_IS_5GHZ_CH(pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_5GHZ_CH(pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_SAP_SAP_SBS_5_1x1; + } else { + /* DBS */ + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_SAP_SAP_DBS_1x1; + else + index = PM_SAP_SAP_DBS_2x2; + } + } + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_sta_go(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].chan == + pm_conc_connection_list[1].chan) { + if (WLAN_REG_IS_24GHZ_CH + (pm_conc_connection_list[0].chan)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_GO_SCC_24_1x1; + else + index = PM_STA_P2P_GO_SCC_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_GO_SCC_5_1x1; + else + index = PM_STA_P2P_GO_SCC_5_2x2; + } + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if ((WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_24GHZ_CH + (pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_GO_MCC_24_1x1; + else + index = PM_STA_P2P_GO_MCC_24_2x2; + } else if ((WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_GO_MCC_5_1x1; + else + index = PM_STA_P2P_GO_MCC_5_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_GO_MCC_24_5_1x1; + else + index = PM_STA_P2P_GO_MCC_24_5_2x2; + } + /* SBS or DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + /* SBS */ + if ((WLAN_REG_IS_5GHZ_CH(pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_5GHZ_CH(pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_GO_SBS_5_1x1; + } else { + /* DBS */ + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_GO_DBS_1x1; + else + index = PM_STA_P2P_GO_DBS_2x2; + } + } + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_sta_cli(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].chan == + pm_conc_connection_list[1].chan) { + if (WLAN_REG_IS_24GHZ_CH + (pm_conc_connection_list[0].chan)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_CLI_SCC_24_1x1; + else + index = PM_STA_P2P_CLI_SCC_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_CLI_SCC_5_1x1; + else + index = PM_STA_P2P_CLI_SCC_5_2x2; + } + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if ((WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_CLI_MCC_24_1x1; + else + index = PM_STA_P2P_CLI_MCC_24_2x2; + } else if ((WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_CLI_MCC_5_1x1; + else + index = PM_STA_P2P_CLI_MCC_5_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_CLI_MCC_24_5_1x1; + else + index = PM_STA_P2P_CLI_MCC_24_5_2x2; + } + /* SBS or DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + /* SBS */ + if ((WLAN_REG_IS_5GHZ_CH(pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_5GHZ_CH(pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_CLI_SBS_5_1x1; + } else { + /* DBS */ + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_P2P_CLI_DBS_1x1; + else + index = PM_STA_P2P_CLI_DBS_2x2; + } + } + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_go_cli(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].chan == + pm_conc_connection_list[1].chan) { + if (WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[0].chan)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_CLI_SCC_24_1x1; + else + index = PM_P2P_GO_P2P_CLI_SCC_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_CLI_SCC_5_1x1; + else + index = PM_P2P_GO_P2P_CLI_SCC_5_2x2; + } + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if ((WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_CLI_MCC_24_1x1; + else + index = PM_P2P_GO_P2P_CLI_MCC_24_2x2; + } else if ((WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_CLI_MCC_5_1x1; + else + index = PM_P2P_GO_P2P_CLI_MCC_5_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_CLI_MCC_24_5_1x1; + else + index = PM_P2P_GO_P2P_CLI_MCC_24_5_2x2; + } + /* SBS or DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + /* SBS */ + if ((WLAN_REG_IS_5GHZ_CH(pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_5GHZ_CH(pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_CLI_SBS_5_1x1; + } else { + /* DBS */ + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_P2P_CLI_DBS_1x1; + else + index = PM_P2P_GO_P2P_CLI_DBS_2x2; + } + } + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_go_sap(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].chan == + pm_conc_connection_list[1].chan) { + if (WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[0].chan)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_SAP_SCC_24_1x1; + else + index = PM_P2P_GO_SAP_SCC_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_SAP_SCC_5_1x1; + else + index = PM_P2P_GO_SAP_SCC_5_2x2; + } + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if ((WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_SAP_MCC_24_1x1; + else + index = PM_P2P_GO_SAP_MCC_24_2x2; + } else if ((WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_SAP_MCC_5_1x1; + else + index = PM_P2P_GO_SAP_MCC_5_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_SAP_MCC_24_5_1x1; + else + index = PM_P2P_GO_SAP_MCC_24_5_2x2; + } + /* SBS or DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + /* SBS */ + if ((WLAN_REG_IS_5GHZ_CH(pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_5GHZ_CH(pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_SAP_SBS_5_1x1; + } else { + /* DBS */ + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_P2P_GO_SAP_DBS_1x1; + else + index = PM_P2P_GO_SAP_DBS_2x2; + } + } + return index; +} + +static enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index_sta_sta(void) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + /* SCC */ + if (pm_conc_connection_list[0].chan == + pm_conc_connection_list[1].chan) { + if (WLAN_REG_IS_24GHZ_CH + (pm_conc_connection_list[0].chan)) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_STA_SCC_24_1x1; + else + index = PM_STA_STA_SCC_24_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_STA_SCC_5_1x1; + else + index = PM_STA_STA_SCC_5_2x2; + } + /* MCC */ + } else if (pm_conc_connection_list[0].mac == + pm_conc_connection_list[1].mac) { + if ((WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_24GHZ_CH( + pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_STA_MCC_24_1x1; + else + index = PM_STA_STA_MCC_24_2x2; + } else if ((WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_5GHZ_CH( + pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_STA_MCC_5_1x1; + else + index = PM_STA_STA_MCC_5_2x2; + } else { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_STA_MCC_24_5_1x1; + else + index = PM_STA_STA_MCC_24_5_2x2; + } + /* SBS or DBS */ + } else if (pm_conc_connection_list[0].mac != + pm_conc_connection_list[1].mac) { + /* SBS */ + if ((WLAN_REG_IS_5GHZ_CH(pm_conc_connection_list[0].chan)) && + (WLAN_REG_IS_5GHZ_CH(pm_conc_connection_list[1].chan))) { + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_STA_SBS_5_1x1; + } else { + /* DBS */ + if (POLICY_MGR_ONE_ONE == + pm_conc_connection_list[0].chain_mask) + index = PM_STA_STA_DBS_1x1; + else + index = PM_STA_STA_DBS_2x2; + } + } + return index; +} + +enum policy_mgr_two_connection_mode + policy_mgr_get_third_connection_pcl_table_index( + struct wlan_objmgr_psoc *psoc) +{ + enum policy_mgr_two_connection_mode index = PM_MAX_TWO_CONNECTION_MODE; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return index; + } + + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (((PM_P2P_CLIENT_MODE == pm_conc_connection_list[0].mode) && + (PM_SAP_MODE == pm_conc_connection_list[1].mode)) || + ((PM_SAP_MODE == pm_conc_connection_list[0].mode) && + (PM_P2P_CLIENT_MODE == pm_conc_connection_list[1].mode))) + index = + policy_mgr_get_third_connection_pcl_table_index_cli_sap(); + else if (((PM_STA_MODE == pm_conc_connection_list[0].mode) && + (PM_SAP_MODE == pm_conc_connection_list[1].mode)) || + ((PM_SAP_MODE == pm_conc_connection_list[0].mode) && + (PM_STA_MODE == pm_conc_connection_list[1].mode))) + index = + policy_mgr_get_third_connection_pcl_table_index_sta_sap(); + else if ((PM_SAP_MODE == pm_conc_connection_list[0].mode) && + (PM_SAP_MODE == pm_conc_connection_list[1].mode)) + index = + policy_mgr_get_third_connection_pcl_table_index_sap_sap(); + else if (((PM_STA_MODE == pm_conc_connection_list[0].mode) && + (PM_P2P_GO_MODE == pm_conc_connection_list[1].mode)) || + ((PM_P2P_GO_MODE == pm_conc_connection_list[0].mode) && + (PM_STA_MODE == pm_conc_connection_list[1].mode))) + index = + policy_mgr_get_third_connection_pcl_table_index_sta_go(); + else if (((PM_STA_MODE == pm_conc_connection_list[0].mode) && + (PM_P2P_CLIENT_MODE == pm_conc_connection_list[1].mode)) || + ((PM_P2P_CLIENT_MODE == pm_conc_connection_list[0].mode) && + (PM_STA_MODE == pm_conc_connection_list[1].mode))) + index = + policy_mgr_get_third_connection_pcl_table_index_sta_cli(); + else if (((PM_P2P_GO_MODE == pm_conc_connection_list[0].mode) && + (PM_P2P_CLIENT_MODE == pm_conc_connection_list[1].mode)) || + ((PM_P2P_CLIENT_MODE == pm_conc_connection_list[0].mode) && + (PM_P2P_GO_MODE == pm_conc_connection_list[1].mode))) + index = + policy_mgr_get_third_connection_pcl_table_index_go_cli(); + else if (((PM_SAP_MODE == pm_conc_connection_list[0].mode) && + (PM_P2P_GO_MODE == pm_conc_connection_list[1].mode)) || + ((PM_P2P_GO_MODE == pm_conc_connection_list[0].mode) && + (PM_SAP_MODE == pm_conc_connection_list[1].mode))) + index = + policy_mgr_get_third_connection_pcl_table_index_go_sap(); + else if (((PM_STA_MODE == pm_conc_connection_list[0].mode) && + (PM_STA_MODE == pm_conc_connection_list[1].mode)) || + ((PM_STA_MODE == pm_conc_connection_list[0].mode) && + (PM_STA_MODE == pm_conc_connection_list[1].mode))) + index = + policy_mgr_get_third_connection_pcl_table_index_sta_sta(); + + policy_mgr_debug("mode0:%d mode1:%d chan0:%d chan1:%d chain:%d index:%d", + pm_conc_connection_list[0].mode, + pm_conc_connection_list[1].mode, + pm_conc_connection_list[0].chan, + pm_conc_connection_list[1].chan, + pm_conc_connection_list[0].chain_mask, index); + + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return index; +} + +uint8_t +policy_mgr_get_nondfs_preferred_channel(struct wlan_objmgr_psoc *psoc, + enum policy_mgr_con_mode mode, + bool for_existing_conn) +{ + uint8_t pcl_channels[QDF_MAX_NUM_CHAN]; + uint8_t pcl_weight[QDF_MAX_NUM_CHAN]; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + /* + * in worst case if we can't find any channel at all + * then return 2.4G channel, so atleast we won't fall + * under 5G MCC scenario + */ + uint8_t channel = PM_24_GHZ_CHANNEL_6; + uint32_t i, pcl_len = 0; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return channel; + } + + if (true == for_existing_conn) { + /* + * First try to see if there is any non-dfs channel already + * present in current connection table. If yes then return + * that channel + */ + if (true == policy_mgr_is_any_nondfs_chnl_present( + psoc, &channel)) + return channel; + + if (QDF_STATUS_SUCCESS != policy_mgr_get_pcl_for_existing_conn( + psoc, mode, + &pcl_channels[0], &pcl_len, + pcl_weight, QDF_ARRAY_SIZE(pcl_weight), + false)) + return channel; + } else { + if (QDF_STATUS_SUCCESS != policy_mgr_get_pcl(psoc, mode, + &pcl_channels[0], &pcl_len, + pcl_weight, QDF_ARRAY_SIZE(pcl_weight))) + return channel; + } + + for (i = 0; i < pcl_len; i++) { + if (wlan_reg_is_dfs_ch(pm_ctx->pdev, pcl_channels[i])) { + continue; + } else { + channel = pcl_channels[i]; + break; + } + } + + return channel; +} + +static void policy_mgr_remove_dsrc_channels(uint8_t *chan_list, + uint32_t *num_channels, + struct wlan_objmgr_pdev *pdev) +{ + uint32_t num_chan_temp = 0; + int i; + + for (i = 0; i < *num_channels; i++) { + if (!wlan_reg_is_dsrc_chan(pdev, chan_list[i])) { + chan_list[num_chan_temp] = chan_list[i]; + num_chan_temp++; + } + } + + *num_channels = num_chan_temp; +} + +QDF_STATUS policy_mgr_get_valid_chans(struct wlan_objmgr_psoc *psoc, + uint8_t *chan_list, uint32_t *list_len) +{ + QDF_STATUS status; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + *list_len = 0; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + if (!pm_ctx->sme_cbacks.sme_get_valid_channels) { + policy_mgr_err("sme_get_valid_chans callback is NULL"); + return QDF_STATUS_E_FAILURE; + } + + *list_len = QDF_MAX_NUM_CHAN; + status = pm_ctx->sme_cbacks.sme_get_valid_channels( + chan_list, list_len); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Error in getting valid channels"); + *list_len = 0; + return status; + } + + policy_mgr_remove_dsrc_channels(chan_list, list_len, pm_ctx->pdev); + + return QDF_STATUS_SUCCESS; +} + +bool policy_mgr_list_has_24GHz_channel(uint8_t *channel_list, + uint32_t list_len) +{ + uint32_t i; + + for (i = 0; i < list_len; i++) { + if (WLAN_REG_IS_24GHZ_CH(channel_list[i])) + return true; + } + + return false; +} + +QDF_STATUS policy_mgr_set_sap_mandatory_channels(struct wlan_objmgr_psoc *psoc, + uint8_t *channels, uint32_t len) +{ + uint32_t i; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + if (!len) { + policy_mgr_err("No mandatory freq/chan configured"); + return QDF_STATUS_E_FAILURE; + } + + if (!policy_mgr_list_has_24GHz_channel(channels, len)) { + policy_mgr_err("2.4GHz channels missing, this is not expected"); + return QDF_STATUS_E_FAILURE; + } + + policy_mgr_debug("mandatory chan length:%d", + pm_ctx->sap_mandatory_channels_len); + + for (i = 0; i < len; i++) { + pm_ctx->sap_mandatory_channels[i] = channels[i]; + policy_mgr_debug("chan:%d", pm_ctx->sap_mandatory_channels[i]); + } + + pm_ctx->sap_mandatory_channels_len = len; + + return QDF_STATUS_SUCCESS; +} + +bool policy_mgr_is_sap_mandatory_channel_set(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return false; + } + + if (pm_ctx->sap_mandatory_channels_len) + return true; + else + return false; +} + +QDF_STATUS policy_mgr_modify_sap_pcl_based_on_mandatory_channel( + struct wlan_objmgr_psoc *psoc, + uint8_t *pcl_list_org, + uint8_t *weight_list_org, + uint32_t *pcl_len_org) +{ + uint32_t i, j, pcl_len = 0; + bool found; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + if (!pm_ctx->sap_mandatory_channels_len) + return QDF_STATUS_SUCCESS; + + if (!policy_mgr_list_has_24GHz_channel(pm_ctx->sap_mandatory_channels, + pm_ctx->sap_mandatory_channels_len)) { + policy_mgr_err("fav channel list is missing 2.4GHz channels"); + return QDF_STATUS_E_FAILURE; + } + + for (i = 0; i < pm_ctx->sap_mandatory_channels_len; i++) + policy_mgr_debug("fav chan:%d", + pm_ctx->sap_mandatory_channels[i]); + + for (i = 0; i < *pcl_len_org; i++) { + found = false; + if (i >= QDF_MAX_NUM_CHAN) { + policy_mgr_debug("index is exceeding QDF_MAX_NUM_CHAN"); + break; + } + for (j = 0; j < pm_ctx->sap_mandatory_channels_len; j++) { + if (pcl_list_org[i] == + pm_ctx->sap_mandatory_channels[j]) { + found = true; + break; + } + } + if (found && (pcl_len < QDF_MAX_NUM_CHAN)) { + pcl_list_org[pcl_len] = pcl_list_org[i]; + weight_list_org[pcl_len++] = weight_list_org[i]; + } + } + *pcl_len_org = pcl_len; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_sap_mandatory_channel(struct wlan_objmgr_psoc *psoc, + uint32_t *chan) +{ + QDF_STATUS status; + struct policy_mgr_pcl_list pcl; + + qdf_mem_zero(&pcl, sizeof(pcl)); + + status = policy_mgr_get_pcl_for_existing_conn(psoc, PM_SAP_MODE, + pcl.pcl_list, &pcl.pcl_len, + pcl.weight_list, QDF_ARRAY_SIZE(pcl.weight_list), + false); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Unable to get PCL for SAP"); + return status; + } + + /* + * Get inside below loop if no existing SAP connection and hence a new + * SAP connection might be coming up. pcl.pcl_len can be 0 if no common + * channel between PCL & mandatory channel list as well + */ + if (!pcl.pcl_len && !policy_mgr_mode_specific_connection_count(psoc, + PM_SAP_MODE, NULL)) { + policy_mgr_debug("policy_mgr_get_pcl_for_existing_conn returned no pcl"); + status = policy_mgr_get_pcl(psoc, PM_SAP_MODE, + pcl.pcl_list, &pcl.pcl_len, + pcl.weight_list, + QDF_ARRAY_SIZE(pcl.weight_list)); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Unable to get PCL for SAP: policy_mgr_get_pcl"); + return status; + } + } + + status = policy_mgr_modify_sap_pcl_based_on_mandatory_channel( + psoc, pcl.pcl_list, + pcl.weight_list, + &pcl.pcl_len); + if (QDF_IS_STATUS_ERROR(status)) { + policy_mgr_err("Unable to modify SAP PCL"); + return status; + } + + if (!pcl.pcl_len) { + policy_mgr_err("No common channel between mandatory list & PCL"); + return QDF_STATUS_E_FAILURE; + } + + *chan = pcl.pcl_list[0]; + policy_mgr_debug("mandatory channel:%d", *chan); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS policy_mgr_get_valid_chan_weights(struct wlan_objmgr_psoc *psoc, + struct policy_mgr_pcl_chan_weights *weight) +{ + uint32_t i, j; + struct policy_mgr_conc_connection_info + info[MAX_NUMBER_OF_CONC_CONNECTIONS] = { {0} }; + uint8_t num_cxn_del = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return QDF_STATUS_E_FAILURE; + } + + qdf_mem_set(weight->weighed_valid_list, QDF_MAX_NUM_CHAN, + WEIGHT_OF_DISALLOWED_CHANNELS); + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + if (policy_mgr_mode_specific_connection_count( + psoc, PM_STA_MODE, NULL) > 0) { + /* + * Store the STA mode's parameter and temporarily delete it + * from the concurrency table. This way the allow concurrency + * check can be used as though a new connection is coming up, + * allowing to detect the disallowed channels. + */ + policy_mgr_store_and_del_conn_info(psoc, PM_STA_MODE, false, + info, &num_cxn_del); + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + /* + * There is a small window between releasing the above lock + * and acquiring the same in policy_mgr_allow_concurrency, + * below! + */ + for (i = 0; i < weight->saved_num_chan; i++) { + if (policy_mgr_is_concurrency_allowed + (psoc, PM_STA_MODE, weight->saved_chan_list[i], + HW_MODE_20_MHZ)) { + weight->weighed_valid_list[i] = + WEIGHT_OF_NON_PCL_CHANNELS; + } + } + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + /* Restore the connection info */ + policy_mgr_restore_deleted_conn_info(psoc, info, num_cxn_del); + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + for (i = 0; i < weight->saved_num_chan; i++) { + for (j = 0; j < weight->pcl_len; j++) { + if (weight->saved_chan_list[i] == weight->pcl_list[j]) { + weight->weighed_valid_list[i] = + weight->weight_list[j]; + break; + } + } + } + + return QDF_STATUS_SUCCESS; +} + +uint8_t policy_mgr_mode_specific_get_channel( + struct wlan_objmgr_psoc *psoc, enum policy_mgr_con_mode mode) +{ + uint32_t conn_index; + uint8_t channel = 0; + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("Invalid Context"); + return channel; + } + /* provides the channel for the first matching mode type */ + qdf_mutex_acquire(&pm_ctx->qdf_conc_list_lock); + for (conn_index = 0; conn_index < MAX_NUMBER_OF_CONC_CONNECTIONS; + conn_index++) { + if ((pm_conc_connection_list[conn_index].mode == mode) && + pm_conc_connection_list[conn_index].in_use) { + channel = pm_conc_connection_list[conn_index].chan; + break; + } + } + qdf_mutex_release(&pm_ctx->qdf_conc_list_lock); + + return channel; +} + +uint8_t policy_mgr_get_alternate_channel_for_sap( + struct wlan_objmgr_psoc *psoc) +{ + uint8_t pcl_channels[QDF_MAX_NUM_CHAN]; + uint8_t pcl_weight[QDF_MAX_NUM_CHAN]; + uint8_t channel = 0; + uint32_t pcl_len = 0; + + if (QDF_STATUS_SUCCESS == policy_mgr_get_pcl(psoc, PM_SAP_MODE, + &pcl_channels[0], &pcl_len, + pcl_weight, QDF_ARRAY_SIZE(pcl_weight))) { + channel = pcl_channels[0]; + } + + return channel; +} diff --git a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_1x1_dbs_i.h b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_1x1_dbs_i.h new file mode 100644 index 000000000000..b1f193fe53d9 --- /dev/null +++ b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_1x1_dbs_i.h @@ -0,0 +1,1173 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_POLICY_MGR_TABLES_1X1_DBS_H +#define __WLAN_POLICY_MGR_TABLES_1X1_DBS_H + +#include "wlan_policy_mgr_api.h" + +/** + * second_connection_pcl_dbs_1x1_table - table which provides PCL + * for the 2nd connection, when we have a connection already in + * the system (with DBS supported by HW) + */ +pm_dbs_pcl_second_connection_table_type +pm_second_connection_pcl_dbs_1x1_table = { + [PM_STA_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = {PM_5G, PM_5G, PM_5G } }, + + [PM_STA_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = {PM_5G, PM_5G, PM_5G } }, + + [PM_STA_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_GO_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_IBSS_MODE] = {PM_24G, PM_24G, PM_24G } }, + + [PM_STA_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_GO_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_IBSS_MODE] = {PM_24G, PM_24G, PM_24G } }, + + [PM_P2P_CLI_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_GO_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_GO_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_GO_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_P2P_GO_MODE] = {PM_SCC_CH_24G, PM_SCC_CH_24G, PM_SCC_CH_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SCC_CH, PM_24G_SCC_CH, PM_24G_SCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SCC_CH, PM_24G_SCC_CH, PM_24G_SCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, +}; + +/** + * third_connection_pcl_dbs_table - table which provides PCL for + * the 3rd connection, when we have two connections already in + * the system (with DBS supported by HW) + */ +static pm_dbs_pcl_third_connection_table_type +pm_third_connection_pcl_dbs_1x1_table = { + [PM_STA_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G_MCC_CH, PM_5G, PM_5G_MCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G_MCC_CH, PM_5G, PM_5G_MCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = {PM_MCC_CH_24G, PM_24G, PM_24G}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = {PM_MCC_CH_24G, PM_24G, PM_24G}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = {PM_MCC_CH_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = {PM_MCC_CH_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_DBS_1x1] = { + [PM_STA_MODE] = { + PM_SCC_ON_5_SCC_ON_24_5G, PM_NONE, PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = {PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_SAP_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_SAP_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_DBS_1x1] = { + [PM_STA_MODE] = { PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = { PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G_MCC_CH, PM_5G, PM_5G_MCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G_MCC_CH, PM_5G, PM_5G_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G_MCC_CH, PM_5G, PM_5G_MCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G_MCC_CH, PM_5G, PM_5G_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_SCC_CH_24G, PM_24G_SCC_CH, PM_SCC_CH_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_5_1x1] = { + [PM_STA_MODE] = {PM_MCC_CH_24G, PM_24G, PM_24G}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MCC_CH_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_5_2x2] = { + [PM_STA_MODE] = {PM_MCC_CH_24G, PM_24G, PM_24G}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MCC_CH_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_5_1x1] = { + [PM_STA_MODE] = {PM_MCC_CH_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MCC_CH_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_5_2x2] = { + [PM_STA_MODE] = {PM_MCC_CH_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_MCC_CH_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_DBS_1x1] = { + [PM_STA_MODE] = { + PM_SCC_ON_5_SCC_ON_24_5G, PM_NONE, PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = {PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_SCC_ON_5_SCC_ON_24_5G, PM_NONE, PM_SCC_ON_5_SCC_ON_24}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + +}; + +/** + * next_action_two_connection_table - table which provides next + * action while a new connection is coming up, with one + * connection already in the system + */ +static policy_mgr_next_action_two_connection_table_type + pm_next_action_two_connection_dbs_1x1_table = { + [PM_STA_24_1x1] = {PM_NOP, PM_DBS}, + [PM_STA_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_STA_5_1x1] = {PM_DBS, PM_NOP}, + [PM_STA_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_P2P_CLI_24_1x1] = {PM_NOP, PM_DBS}, + [PM_P2P_CLI_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_P2P_CLI_5_1x1] = {PM_DBS, PM_NOP}, + [PM_P2P_CLI_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_P2P_GO_24_1x1] = {PM_NOP, PM_DBS}, + [PM_P2P_GO_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_P2P_GO_5_1x1] = {PM_DBS, PM_NOP}, + [PM_P2P_GO_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_SAP_24_1x1] = {PM_NOP, PM_DBS}, + [PM_SAP_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_SAP_5_1x1] = {PM_DBS, PM_NOP}, + [PM_SAP_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_IBSS_24_1x1] = {PM_NOP, PM_DBS}, + [PM_IBSS_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_IBSS_5_1x1] = {PM_DBS, PM_NOP}, + [PM_IBSS_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, +}; + +/** + * next_action_three_connection_table - table which provides next + * action while a new connection is coming up, with two + * connections already in the system + */ +static policy_mgr_next_action_three_connection_table_type + pm_next_action_three_connection_dbs_1x1_table = { + [PM_STA_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_STA_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_STA_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_STA_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_STA_SAP_SCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_STA_SAP_SCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_STA_SAP_MCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_STA_SAP_MCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_STA_SAP_MCC_24_5_1x1] = {PM_DBS, PM_DBS}, + [PM_STA_SAP_MCC_24_5_2x2] = {PM_DBS_DOWNGRADE, PM_DBS_DOWNGRADE}, + [PM_STA_SAP_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_GO_SCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_STA_P2P_GO_SCC_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_STA_P2P_GO_MCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_STA_P2P_GO_MCC_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_STA_P2P_GO_SCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_STA_P2P_GO_SCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_STA_P2P_GO_MCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_STA_P2P_GO_MCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_STA_P2P_GO_MCC_24_5_1x1] = {PM_DBS, PM_DBS}, + [PM_STA_P2P_GO_MCC_24_5_2x2] = { + PM_DBS_DOWNGRADE, PM_DBS_DOWNGRADE}, + [PM_STA_P2P_GO_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_CLI_SCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_STA_P2P_CLI_SCC_24_2x2] = { + PM_NOP, PM_DBS_DOWNGRADE}, + [PM_STA_P2P_CLI_MCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_STA_P2P_CLI_MCC_24_2x2] = { + PM_NOP, PM_DBS_DOWNGRADE}, + [PM_STA_P2P_CLI_SCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_STA_P2P_CLI_SCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_STA_P2P_CLI_MCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_STA_P2P_CLI_MCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_STA_P2P_CLI_MCC_24_5_1x1] = {PM_DBS, PM_DBS}, + [PM_STA_P2P_CLI_MCC_24_5_2x2] = { + PM_DBS_DOWNGRADE, PM_DBS_DOWNGRADE}, + [PM_STA_P2P_CLI_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_P2P_CLI_SCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_P2P_GO_P2P_CLI_SCC_24_2x2] = { + PM_NOP, PM_DBS_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_MCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_P2P_GO_P2P_CLI_MCC_24_2x2] = { + PM_NOP, PM_DBS_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_SCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_P2P_GO_P2P_CLI_SCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_24_5_1x1] = {PM_DBS, PM_DBS}, + [PM_P2P_GO_P2P_CLI_MCC_24_5_2x2] = { + PM_DBS_DOWNGRADE, PM_DBS_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_P2P_GO_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_P2P_GO_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_P2P_GO_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_P2P_GO_SAP_SCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_P2P_GO_SAP_SCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_P2P_GO_SAP_MCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_P2P_GO_SAP_MCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_P2P_GO_SAP_MCC_24_5_1x1] = {PM_DBS, PM_DBS}, + [PM_P2P_GO_SAP_MCC_24_5_2x2] = { + PM_DBS_DOWNGRADE, PM_DBS_DOWNGRADE}, + [PM_P2P_GO_SAP_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_P2P_CLI_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_P2P_CLI_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_P2P_CLI_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_P2P_CLI_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS_DOWNGRADE}, + [PM_P2P_CLI_SAP_SCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_P2P_CLI_SAP_SCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_P2P_CLI_SAP_MCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_P2P_CLI_SAP_MCC_5_2x2] = {PM_DBS_DOWNGRADE, PM_NOP}, + [PM_P2P_CLI_SAP_MCC_24_5_1x1] = {PM_DBS, PM_DBS}, + [PM_P2P_CLI_SAP_MCC_24_5_2x2] = {PM_DBS_DOWNGRADE, PM_DBS_DOWNGRADE}, + [PM_P2P_CLI_SAP_DBS_1x1] = {PM_NOP, PM_NOP}, + + [PM_SAP_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_SAP_SAP_SCC_5_1x1] = {PM_DBS, PM_NOP}, + +}; + +#endif diff --git a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_2x2_2g_1x1_5g.h b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_2x2_2g_1x1_5g.h new file mode 100644 index 000000000000..54d4c8ab80c6 --- /dev/null +++ b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_2x2_2g_1x1_5g.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_POLICY_MGR_TABLES_2X2_2G_1X1_5G_DBS_H +#define __WLAN_POLICY_MGR_TABLES_2X2_2G_1X1_5G_DBS_H + +#include "wlan_policy_mgr_api.h" + +/** + * next_action_two_connection_table - table which provides next + * action while a new connection is coming up, with one + * connection already in the system + */ +static policy_mgr_next_action_two_connection_table_type + pm_next_action_two_connection_dbs_2x2_2g_1x1_5g_table = { + [PM_STA_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_STA_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_STA_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_STA_5_2x2] = {PM_DBS2, PM_NOP}, + [PM_P2P_CLI_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_P2P_CLI_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_P2P_CLI_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_P2P_CLI_5_2x2] = {PM_DBS2, PM_NOP}, + [PM_P2P_GO_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_P2P_GO_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_SAP_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_SAP_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_SAP_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_SAP_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_IBSS_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_IBSS_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_IBSS_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_IBSS_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, +}; + +/** + * next_action_three_connection_table - table which provides next + * action while a new connection is coming up, with two + * connections already in the system + */ +static policy_mgr_next_action_three_connection_table_type + pm_next_action_three_connection_dbs_2x2_2g_1x1_5g_table = { + [PM_STA_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_STA_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_STA_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_STA_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_STA_SAP_SCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_STA_SAP_SCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_STA_SAP_MCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_STA_SAP_MCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_STA_SAP_MCC_24_5_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_STA_SAP_MCC_24_5_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_STA_SAP_DBS_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_STA_SAP_DBS_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + + [PM_STA_P2P_GO_SCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_GO_SCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_GO_MCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_GO_MCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_GO_SCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_STA_P2P_GO_SCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_STA_P2P_GO_MCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_STA_P2P_GO_MCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_STA_P2P_GO_MCC_24_5_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_STA_P2P_GO_MCC_24_5_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_STA_P2P_GO_DBS_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_STA_P2P_GO_DBS_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + + [PM_STA_P2P_CLI_SCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_CLI_SCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_CLI_MCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_CLI_MCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_CLI_SCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_STA_P2P_CLI_SCC_5_2x2] = {PM_DBS2, PM_NOP}, + [PM_STA_P2P_CLI_MCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_STA_P2P_CLI_MCC_5_2x2] = {PM_DBS2, PM_NOP}, + [PM_STA_P2P_CLI_MCC_24_5_1x1] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_CLI_MCC_24_5_2x2] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_CLI_DBS_1x1] = {PM_NOP, PM_DBS2}, + [PM_STA_P2P_CLI_DBS_2x2] = {PM_NOP, PM_DBS2}, + + [PM_P2P_GO_P2P_CLI_SCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_P2P_CLI_SCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_P2P_CLI_MCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_P2P_CLI_MCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_P2P_CLI_SCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_P2P_GO_P2P_CLI_SCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_24_5_1x1] = {PM_DBS2_DOWNGRADE, + PM_DBS2_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_MCC_24_5_2x2] = {PM_DBS2_DOWNGRADE, + PM_DBS2_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_DBS_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_DBS_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + + [PM_P2P_GO_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_P2P_GO_SAP_SCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_P2P_GO_SAP_SCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_P2P_GO_SAP_MCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_P2P_GO_SAP_MCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_P2P_GO_SAP_MCC_24_5_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_P2P_GO_SAP_MCC_24_5_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_P2P_GO_SAP_DBS_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_P2P_GO_SAP_DBS_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + + [PM_P2P_CLI_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_P2P_CLI_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_P2P_CLI_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_P2P_CLI_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_P2P_CLI_SAP_SCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_P2P_CLI_SAP_SCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_P2P_CLI_SAP_MCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_P2P_CLI_SAP_MCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_P2P_CLI_SAP_MCC_24_5_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_P2P_CLI_SAP_MCC_24_5_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_P2P_CLI_SAP_DBS_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_P2P_CLI_SAP_DBS_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + + [PM_SAP_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_SAP_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_SAP_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS2}, + [PM_SAP_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS2}, + [PM_SAP_SAP_SCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_SAP_SAP_SCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_SAP_SAP_MCC_5_1x1] = {PM_DBS2, PM_NOP}, + [PM_SAP_SAP_MCC_5_2x2] = {PM_DBS2_DOWNGRADE, PM_NOP}, + [PM_SAP_SAP_MCC_24_5_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_SAP_SAP_MCC_24_5_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_SAP_SAP_DBS_1x1] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, + [PM_SAP_SAP_DBS_2x2] = {PM_DBS2_DOWNGRADE, PM_DBS2_DOWNGRADE}, +}; + +#endif diff --git a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_2x2_5g_1x1_2g.h b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_2x2_5g_1x1_2g.h new file mode 100644 index 000000000000..bdf8223d8fbb --- /dev/null +++ b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_2x2_5g_1x1_2g.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_POLICY_MGR_TABLES_2X2_5G_1X1_2G_DBS_H +#define __WLAN_POLICY_MGR_TABLES_2X2_5G_1X1_2G_DBS_H + +#include "wlan_policy_mgr_api.h" + +/** + * next_action_two_connection_table - table which provides next + * action while a new connection is coming up, with one + * connection already in the system + */ +static policy_mgr_next_action_two_connection_table_type + pm_next_action_two_connection_dbs_2x2_5g_1x1_2g_table = { + [PM_STA_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_STA_24_2x2] = {PM_NOP, PM_DBS1}, + [PM_STA_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_STA_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_P2P_CLI_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_P2P_CLI_24_2x2] = {PM_NOP, PM_DBS1}, + [PM_P2P_CLI_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_P2P_CLI_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_P2P_GO_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_SAP_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_SAP_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_SAP_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_SAP_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_IBSS_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_IBSS_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_IBSS_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_IBSS_5_2x2] = {PM_DBS1, PM_NOP}, +}; + +/** + * next_action_three_connection_table - table which provides next + * action while a new connection is coming up, with two + * connections already in the system + */ +static policy_mgr_next_action_three_connection_table_type + pm_next_action_three_connection_dbs_2x2_5g_1x1_2g_table = { + [PM_STA_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_STA_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_STA_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_STA_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_STA_SAP_SCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_STA_SAP_SCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_STA_SAP_MCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_STA_SAP_MCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_STA_SAP_MCC_24_5_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_STA_SAP_MCC_24_5_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_STA_SAP_DBS_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_STA_SAP_DBS_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + + [PM_STA_P2P_GO_SCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_STA_P2P_GO_SCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_STA_P2P_GO_MCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_STA_P2P_GO_MCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_STA_P2P_GO_SCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_STA_P2P_GO_SCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_STA_P2P_GO_MCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_STA_P2P_GO_MCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_STA_P2P_GO_MCC_24_5_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_STA_P2P_GO_MCC_24_5_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_STA_P2P_GO_DBS_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_STA_P2P_GO_DBS_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + + [PM_STA_P2P_CLI_SCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_STA_P2P_CLI_SCC_24_2x2] = {PM_NOP, PM_DBS1}, + [PM_STA_P2P_CLI_MCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_STA_P2P_CLI_MCC_24_2x2] = {PM_NOP, PM_DBS1}, + [PM_STA_P2P_CLI_SCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_STA_P2P_CLI_SCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_STA_P2P_CLI_MCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_STA_P2P_CLI_MCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_STA_P2P_CLI_MCC_24_5_1x1] = {PM_DBS1, PM_DBS1}, + [PM_STA_P2P_CLI_MCC_24_5_2x2] = {PM_DBS1, PM_DBS1}, + [PM_STA_P2P_CLI_DBS_1x1] = {PM_DBS1, PM_DBS1}, + [PM_STA_P2P_CLI_DBS_2x2] = {PM_DBS1, PM_DBS1}, + + [PM_P2P_GO_P2P_CLI_SCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_P2P_GO_P2P_CLI_SCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_MCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_P2P_GO_P2P_CLI_MCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_SCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_P2P_CLI_SCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_24_5_1x1] = {PM_DBS1_DOWNGRADE, + PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_MCC_24_5_2x2] = {PM_DBS1_DOWNGRADE, + PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_DBS_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_DBS_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + + [PM_P2P_GO_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_P2P_GO_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_P2P_GO_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_SAP_SCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_SAP_SCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_SAP_MCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_SAP_MCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_P2P_GO_SAP_MCC_24_5_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_SAP_MCC_24_5_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_SAP_DBS_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_P2P_GO_SAP_DBS_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + + [PM_P2P_CLI_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_P2P_CLI_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_P2P_CLI_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_P2P_CLI_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_P2P_CLI_SAP_SCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_P2P_CLI_SAP_SCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_P2P_CLI_SAP_MCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_P2P_CLI_SAP_MCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_P2P_CLI_SAP_MCC_24_5_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_P2P_CLI_SAP_MCC_24_5_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_P2P_CLI_SAP_DBS_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_P2P_CLI_SAP_DBS_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + + [PM_SAP_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_SAP_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_SAP_SAP_MCC_24_1x1] = {PM_NOP, PM_DBS1}, + [PM_SAP_SAP_MCC_24_2x2] = {PM_NOP, PM_DBS1_DOWNGRADE}, + [PM_SAP_SAP_SCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_SAP_SAP_SCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_SAP_SAP_MCC_5_1x1] = {PM_DBS1, PM_NOP}, + [PM_SAP_SAP_MCC_5_2x2] = {PM_DBS1, PM_NOP}, + [PM_SAP_SAP_MCC_24_5_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_SAP_SAP_MCC_24_5_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_SAP_SAP_DBS_1x1] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, + [PM_SAP_SAP_DBS_2x2] = {PM_DBS1_DOWNGRADE, PM_DBS1_DOWNGRADE}, +}; + +#endif diff --git a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_2x2_dbs_i.h b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_2x2_dbs_i.h new file mode 100644 index 000000000000..370c38cff509 --- /dev/null +++ b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_2x2_dbs_i.h @@ -0,0 +1,1394 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WLAN_POLICY_MGR_TABLES_2X2_DBS_H +#define __WLAN_POLICY_MGR_TABLES_2X2_DBS_H + +#include "wlan_policy_mgr_api.h" + +/** + * second_connection_pcl_dbs_2x2_table - table which provides PCL + * for the 2nd connection, when we have a connection already in + * the system (with DBS supported by HW) + * This table consolidates selection for P2PCLI, P2PGO, STA, SAP + * into the single set of STA entries for 2.4G and 5G. + */ +static pm_dbs_pcl_second_connection_table_type +pm_second_connection_pcl_dbs_2x2_table = { + [PM_STA_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = {PM_5G, PM_5G, PM_5G} }, + + [PM_STA_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = {PM_5G, PM_5G, PM_5G} }, + + [PM_STA_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_GO_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_IBSS_MODE] = {PM_24G, PM_24G, PM_24G} }, + + [PM_STA_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_GO_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_IBSS_MODE] = {PM_24G, PM_24G, PM_24G} }, + + [PM_P2P_CLI_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_GO_MODE] = {PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_GO_MODE] = {PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_GO_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE} }, + + [PM_SAP_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE} }, + + [PM_SAP_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE} }, + + [PM_SAP_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_GO_MODE] = {PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE} }, + + [PM_SAP_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_GO_MODE] = {PM_24G_SCC_CH_SBS_CH, PM_24G_SCC_CH_SBS_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_IBSS_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE} }, + + + [PM_IBSS_24_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_5_1x1] = { + [PM_STA_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_5_2x2] = { + [PM_STA_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + +}; + +/** + * third_connection_pcl_dbs_table - table which provides PCL for + * the 3rd connection, when we have two connections already in + * the system (with DBS supported by HW) + */ +static pm_dbs_pcl_third_connection_table_type +pm_third_connection_pcl_dbs_2x2_table = { + [PM_STA_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_DBS_1x1] = { + [PM_STA_MODE] = { PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = { PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_DBS_2x2] = { + [PM_STA_MODE] = { PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = { PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_SBS_CH_5G, PM_SBS_CH, PM_SBS_CH}, + [PM_SAP_MODE] = { + PM_SBS_CH_5G, PM_SBS_CH, PM_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_SBS_CH_5G, PM_SBS_CH, PM_SBS_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_24G, PM_24G, PM_24G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_SAP_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_SAP_MODE] = {PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_DBS_1x1] = { + [PM_STA_MODE] = { PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = { PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_DBS_2x2] = { + [PM_STA_MODE] = { PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = { PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = {PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_SAP_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_SBS_CH_5G, PM_SBS_CH, PM_SBS_CH}, + [PM_SAP_MODE] = { + PM_SBS_CH_5G, PM_SBS_CH, PM_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, + PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_DBS_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_SBS_CH_5G, PM_SBS_CH, PM_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = { PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_DBS_1x1] = { + [PM_STA_MODE] = { + PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = { + PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_DBS_2x2] = { + [PM_STA_MODE] = { + PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = { + PM_SCC_ON_5_SCC_ON_24, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_SBS_CH_5G, PM_SBS_CH, PM_SBS_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, }, + + [PM_STA_P2P_CLI_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_DBS_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_DBS_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = { + PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_SAP_MODE] = { + PM_5G_SCC_CH, PM_5G_SCC_CH, PM_5G_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_SAP_MODE] = {PM_5G, PM_5G, PM_5G}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_SAP_MODE] = {PM_24G_SCC_CH_SBS_CH_5G, PM_24G_SCC_CH, + PM_24G_SCC_CH_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_SAP_MODE] = { + PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_SAP_MODE] = { + PM_24G_SBS_CH_MCC_CH, PM_24G, PM_24G_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, }, + + [PM_STA_STA_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_DBS_1x1] = { + [PM_STA_MODE] = {PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = {PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_DBS_2x2] = { + [PM_STA_MODE] = {PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_SAP_MODE] = {PM_SCC_ON_5_SCC_ON_24_5G, PM_SCC_ON_5_SCC_ON_24, + PM_SCC_ON_5_SCC_ON_24}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_STA_SBS_5_1x1] = { + [PM_STA_MODE] = { + PM_SBS_CH_5G, PM_SBS_CH, PM_SBS_CH}, + [PM_SAP_MODE] = { + PM_SBS_CH_5G, PM_SBS_CH, PM_SBS_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + +}; + +/** + * next_action_two_connection_table - table which provides next + * action while a new connection is coming up, with one + * connection already in the system + */ +static policy_mgr_next_action_two_connection_table_type + pm_next_action_two_connection_dbs_2x2_table = { + [PM_STA_24_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_24_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_5_1x1] = {PM_DBS, PM_SBS}, + [PM_STA_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_P2P_CLI_24_1x1] = {PM_NOP, PM_NOP}, + [PM_P2P_CLI_24_2x2] = {PM_NOP, PM_NOP}, + [PM_P2P_CLI_5_1x1] = {PM_DBS, PM_SBS}, + [PM_P2P_CLI_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_P2P_GO_24_1x1] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_24_2x2] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_5_1x1] = {PM_DBS, PM_SBS}, + [PM_P2P_GO_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_SAP_24_1x1] = {PM_NOP, PM_NOP}, + [PM_SAP_24_2x2] = {PM_NOP, PM_NOP}, + [PM_SAP_5_1x1] = {PM_DBS, PM_SBS}, + [PM_SAP_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_IBSS_24_1x1] = {PM_NOP, PM_NOP}, + [PM_IBSS_24_2x2] = {PM_NOP, PM_NOP}, + [PM_IBSS_5_1x1] = {PM_DBS, PM_NOP}, + [PM_IBSS_5_2x2] = {PM_DBS, PM_NOP}, +}; + +/** + * next_action_three_connection_table - table which provides next + * action while a new connection is coming up, with two + * connections already in the system + */ +static policy_mgr_next_action_three_connection_table_type + pm_next_action_three_connection_dbs_2x2_table = { + [PM_STA_SAP_SCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_SAP_SCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_SAP_MCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_SAP_MCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_SAP_SCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_STA_SAP_SCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_STA_SAP_MCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_STA_SAP_MCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_STA_SAP_MCC_24_5_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_SAP_MCC_24_5_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_SAP_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_SAP_DBS_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_SAP_SBS_5_1x1] = {PM_DBS_UPGRADE, PM_NOP}, + + [PM_STA_P2P_GO_SCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_GO_SCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_GO_MCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_GO_MCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_GO_SCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_STA_P2P_GO_SCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_STA_P2P_GO_MCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_STA_P2P_GO_MCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_STA_P2P_GO_MCC_24_5_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_GO_MCC_24_5_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_GO_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_GO_DBS_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_GO_SBS_5_1x1] = {PM_DBS_UPGRADE, PM_NOP}, + + [PM_STA_P2P_CLI_SCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_CLI_SCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_CLI_MCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_CLI_MCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_CLI_SCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_STA_P2P_CLI_SCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_STA_P2P_CLI_MCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_STA_P2P_CLI_MCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_STA_P2P_CLI_MCC_24_5_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_CLI_MCC_24_5_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_CLI_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_CLI_DBS_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_P2P_CLI_SBS_5_1x1] = {PM_DBS_UPGRADE, PM_NOP}, + + [PM_P2P_GO_P2P_CLI_SCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_P2P_CLI_SCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_P2P_CLI_SCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_P2P_GO_P2P_CLI_SCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_MCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_P2P_GO_P2P_CLI_MCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_P2P_GO_P2P_CLI_MCC_24_5_1x1] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_P2P_CLI_MCC_24_5_2x2] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_P2P_CLI_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_P2P_CLI_DBS_2x2] = {PM_NOP, PM_NOP}, + [PM_P2P_GO_P2P_CLI_SBS_5_1x1] = {PM_DBS_UPGRADE, PM_NOP}, + + [PM_STA_STA_SCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_STA_SCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_STA_MCC_24_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_STA_MCC_24_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_STA_SCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_STA_STA_SCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_STA_STA_MCC_5_1x1] = {PM_DBS, PM_SBS}, + [PM_STA_STA_MCC_5_2x2] = {PM_DBS, PM_SBS_DOWNGRADE}, + [PM_STA_STA_MCC_24_5_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_STA_MCC_24_5_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_STA_DBS_1x1] = {PM_NOP, PM_NOP}, + [PM_STA_STA_DBS_2x2] = {PM_NOP, PM_NOP}, + [PM_STA_STA_SBS_5_1x1] = {PM_DBS_UPGRADE, PM_NOP}, + + [PM_SAP_SAP_SCC_24_1x1] = {PM_NOP, PM_DBS}, + [PM_SAP_SAP_SCC_24_2x2] = {PM_NOP, PM_DBS}, + [PM_SAP_SAP_SCC_5_1x1] = {PM_DBS, PM_NOP}, + [PM_SAP_SAP_SCC_5_2x2] = {PM_DBS, PM_NOP}, + +}; + +#endif diff --git a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_no_dbs_i.h b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_no_dbs_i.h new file mode 100644 index 000000000000..204185247d07 --- /dev/null +++ b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_tables_no_dbs_i.h @@ -0,0 +1,900 @@ +/* + * Copyright (c) 2012-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. + */ + +#ifndef __WLAN_POLICY_MGR_TABLES_NO_DBS_H +#define __WLAN_POLICY_MGR_TABLES_NO_DBS_H + +#include "wlan_policy_mgr_api.h" + +/** + * second_connection_pcl_nodbs_table - table which provides PCL + * for the 2nd connection, when we have a connection already in + * the system (with DBS not supported by HW) + */ +static const enum policy_mgr_pcl_type +second_connection_pcl_nodbs_table[PM_MAX_ONE_CONNECTION_MODE] + [PM_MAX_NUM_OF_MODE][PM_MAX_CONC_PRIORITY_MODE] = { + [PM_STA_24_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_24_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_P2P_CLIENT_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_P2P_GO_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_P2P_CLIENT_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_P2P_GO_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH_5G}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_24_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_24_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_24_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_24_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_SAP_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_IBSS_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, +}; + +/** + * third_connection_pcl_nodbs_table - table which provides PCL + * for the 3rd connection, when we have two connections already + * in the system (with DBS not supported by HW) + */ +static const enum policy_mgr_pcl_type +third_connection_pcl_nodbs_table[PM_MAX_TWO_CONNECTION_MODE] + [PM_MAX_NUM_OF_MODE][PM_MAX_CONC_PRIORITY_MODE] = { + [PM_STA_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_SAP_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_SAP_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_SAP_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_SAP_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_SAP_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_SAP_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_SAP_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_GO_MODE] = { + PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_GO_MODE] = { + PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_5_1x1] = { + [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_5_2x2] = { + [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_5_1x1] = { + [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_MCC_24_5_2x2] = { + [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_GO_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_5G_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_STA_P2P_CLI_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_5G, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_5G_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_1x1] = { + [PM_STA_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_SAP_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_GO_MODE] = { + PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_2x2] = { + [PM_STA_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_SAP_MODE] = {PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_CLIENT_MODE] = { + PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_GO_MODE] = { + PM_5G_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_5_1x1] = { + [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_SCC_5_2x2] = { + [PM_STA_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_SAP_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_P2P_GO_MODE] = {PM_SCC_CH, PM_SCC_CH, PM_SCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_5_1x1] = { + [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_SAP_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_5_2x2] = { + [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_SAP_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_5_1x1] = { + [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_SAP_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_MCC_24_5_2x2] = { + [PM_STA_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_SAP_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_CLIENT_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_P2P_GO_MODE] = {PM_MCC_CH, PM_MCC_CH, PM_MCC_CH}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_P2P_CLI_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_GO_SAP_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + + [PM_P2P_CLI_SAP_SCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_SCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_MCC_24_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_MCC_24_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_SCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_SCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_MCC_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_MCC_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_MCC_24_5_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_MCC_24_5_2x2] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = {PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + + [PM_P2P_CLI_SAP_DBS_1x1] = { + [PM_STA_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_SAP_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_CLIENT_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_P2P_GO_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE}, + [PM_IBSS_MODE] = { + PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE, PM_MAX_PCL_TYPE} }, + +}; + +#endif diff --git a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_ucfg.c b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_ucfg.c new file mode 100644 index 000000000000..310019ba3249 --- /dev/null +++ b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_ucfg.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +#include "wlan_policy_mgr_ucfg.h" +#include "wlan_policy_mgr_i.h" +#include "cfg_ucfg_api.h" +#include "wlan_policy_mgr_api.h" + +static QDF_STATUS policy_mgr_init_cfg(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + struct policy_mgr_cfg *cfg; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return QDF_STATUS_E_FAILURE; + } + cfg = &pm_ctx->cfg; + + cfg->mcc_to_scc_switch = cfg_get(psoc, CFG_MCC_TO_SCC_SWITCH); + cfg->sys_pref = cfg_get(psoc, CFG_CONC_SYS_PREF); + cfg->max_conc_cxns = cfg_get(psoc, CFG_MAX_CONC_CXNS); + cfg->conc_rule1 = cfg_get(psoc, CFG_ENABLE_CONC_RULE1); + cfg->conc_rule2 = cfg_get(psoc, CFG_ENABLE_CONC_RULE2); + cfg->dbs_selection_plcy = cfg_get(psoc, CFG_DBS_SELECTION_PLCY); + cfg->vdev_priority_list = cfg_get(psoc, CFG_VDEV_CUSTOM_PRIORITY_LIST); + cfg->chnl_select_plcy = cfg_get(psoc, CFG_CHNL_SELECT_LOGIC_CONC); + cfg->enable_mcc_adaptive_sch = + cfg_get(psoc, CFG_ENABLE_MCC_ADATIVE_SCH_ENABLED_NAME); + cfg->enable_sta_cxn_5g_band = + cfg_get(psoc, CFG_ENABLE_STA_CONNECTION_IN_5GHZ); + + return QDF_STATUS_SUCCESS; +} + +static void policy_mgr_deinit_cfg(struct wlan_objmgr_psoc *psoc) +{ + struct policy_mgr_psoc_priv_obj *pm_ctx; + + pm_ctx = policy_mgr_get_context(psoc); + if (!pm_ctx) { + policy_mgr_err("pm_ctx is NULL"); + return; + } + + qdf_mem_zero(&pm_ctx->cfg, sizeof(pm_ctx->cfg)); +} + +QDF_STATUS ucfg_policy_mgr_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + + status = policy_mgr_init_cfg(psoc); + if (status != QDF_STATUS_SUCCESS) { + policy_mgr_err("pm_ctx is NULL"); + return status; + } + return QDF_STATUS_SUCCESS; +} + +void ucfg_policy_mgr_psoc_close(struct wlan_objmgr_psoc *psoc) +{ + policy_mgr_deinit_cfg(psoc); +} + +QDF_STATUS ucfg_policy_mgr_get_mcc_scc_switch(struct wlan_objmgr_psoc *psoc, + uint8_t *mcc_scc_switch) +{ + return policy_mgr_get_mcc_scc_switch(psoc, mcc_scc_switch); +} + +QDF_STATUS ucfg_policy_mgr_get_sys_pref(struct wlan_objmgr_psoc *psoc, + uint8_t *sys_pref) +{ + return policy_mgr_get_sys_pref(psoc, sys_pref); +} + +QDF_STATUS ucfg_policy_mgr_set_sys_pref(struct wlan_objmgr_psoc *psoc, + uint8_t sys_pref) +{ + return policy_mgr_set_sys_pref(psoc, sys_pref); +} + +QDF_STATUS ucfg_policy_mgr_get_max_conc_cxns(struct wlan_objmgr_psoc *psoc, + uint8_t *max_conc_cxns) +{ + return policy_mgr_get_max_conc_cxns(psoc, max_conc_cxns);; +} + +QDF_STATUS ucfg_policy_mgr_get_conc_rule1(struct wlan_objmgr_psoc *psoc, + uint8_t *conc_rule1) +{ + return policy_mgr_get_conc_rule1(psoc, conc_rule1); +} + +QDF_STATUS ucfg_policy_mgr_get_conc_rule2(struct wlan_objmgr_psoc *psoc, + uint8_t *conc_rule2) +{ + return policy_mgr_get_conc_rule2(psoc, conc_rule2); +} + +QDF_STATUS ucfg_policy_mgr_get_dbs_selection_plcy(struct wlan_objmgr_psoc *psoc, + uint32_t *dbs_selection_plcy) +{ + return policy_mgr_get_dbs_selection_plcy(psoc, dbs_selection_plcy); +} + +QDF_STATUS ucfg_policy_mgr_get_vdev_priority_list(struct wlan_objmgr_psoc *psoc, + uint32_t *vdev_priority_list) +{ + return policy_mgr_get_vdev_priority_list(psoc, vdev_priority_list); +} + +QDF_STATUS ucfg_policy_mgr_get_chnl_select_plcy(struct wlan_objmgr_psoc *psoc, + uint32_t *chnl_select_plcy) +{ + return policy_mgr_get_chnl_select_plcy(psoc, chnl_select_plcy); +} + + +QDF_STATUS ucfg_policy_mgr_get_mcc_adaptive_sch(struct wlan_objmgr_psoc *psoc, + uint8_t *mcc_adaptive_sch) +{ + return policy_mgr_get_mcc_adaptive_sch(psoc, mcc_adaptive_sch); +} + +QDF_STATUS ucfg_policy_mgr_get_sta_cxn_5g_band(struct wlan_objmgr_psoc *psoc, + uint8_t *enable_sta_cxn_5g_band) +{ + return policy_mgr_get_sta_cxn_5g_band(psoc, enable_sta_cxn_5g_band); +} diff --git a/components/mlme/core/inc/wlan_mlme_main.h b/components/mlme/core/inc/wlan_mlme_main.h index 47f4f8a0a67d..a21089abac1e 100644 --- a/components/mlme/core/inc/wlan_mlme_main.h +++ b/components/mlme/core/inc/wlan_mlme_main.h @@ -181,6 +181,9 @@ QDF_STATUS mlme_cfg_on_psoc_enable(struct wlan_objmgr_psoc *psoc); * * Return: pointer to MLME object */ -struct wlan_mlme_psoc_obj *mlme_get_psoc_obj(struct wlan_objmgr_psoc *psoc); +#define mlme_get_psoc_obj(psoc) mlme_get_psoc_obj_fl(psoc, __func__, __LINE__) +struct wlan_mlme_psoc_obj *mlme_get_psoc_obj_fl(struct wlan_objmgr_psoc *psoc, + const char *func, + uint32_t line); #endif diff --git a/components/mlme/core/src/wlan_mlme_main.c b/components/mlme/core/src/wlan_mlme_main.c index f538a26d3f7d..e21b3a854c4b 100644 --- a/components/mlme/core/src/wlan_mlme_main.c +++ b/components/mlme/core/src/wlan_mlme_main.c @@ -28,13 +28,16 @@ #define NUM_OF_SOUNDING_DIMENSIONS 1 /*Nss - 1, (Nss = 2 for 2x2)*/ -struct wlan_mlme_psoc_obj *mlme_get_psoc_obj(struct wlan_objmgr_psoc *psoc) +struct wlan_mlme_psoc_obj *mlme_get_psoc_obj_fl(struct wlan_objmgr_psoc *psoc, + const char *func, uint32_t line) { struct wlan_mlme_psoc_obj *mlme_obj; mlme_obj = (struct wlan_mlme_psoc_obj *) wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_MLME); + if (!mlme_obj) + mlme_err("mlme obj is null, %s:%d", func, line); return mlme_obj; } @@ -585,6 +588,8 @@ static void mlme_init_timeout_cfg(struct wlan_objmgr_psoc *psoc, cfg_get(psoc, CFG_AP_LINK_MONITOR_TIMEOUT); timeouts->ps_data_inactivity_timeout = cfg_get(psoc, CFG_PS_DATA_INACTIVITY_TIMEOUT); + timeouts->wmi_wq_watchdog_timeout = + cfg_get(psoc, CFG_WMI_WQ_WATCHDOG); } static void mlme_init_ht_cap_in_cfg(struct wlan_objmgr_psoc *psoc, @@ -850,8 +855,19 @@ static void mlme_init_rates_in_cfg(struct wlan_objmgr_psoc *psoc, static void mlme_init_dfs_cfg(struct wlan_objmgr_psoc *psoc, struct wlan_mlme_dfs_cfg *dfs_cfg) { - dfs_cfg->dfs_master_capable = cfg_get(psoc, - CFG_ENABLE_DFS_MASTER_CAPABILITY); + dfs_cfg->dfs_ignore_cac = cfg_get(psoc, CFG_IGNORE_CAC); + dfs_cfg->dfs_master_capable = + cfg_get(psoc, CFG_ENABLE_DFS_MASTER_CAPABILITY); + dfs_cfg->dfs_disable_channel_switch = + cfg_get(psoc, CFG_DISABLE_DFS_CH_SWITCH); + dfs_cfg->dfs_filter_offload = + cfg_get(psoc, CFG_ENABLE_DFS_PHYERR_FILTEROFFLOAD); + dfs_cfg->dfs_prefer_non_dfs = + cfg_get(psoc, CFG_ENABLE_NON_DFS_CHAN_ON_RADAR); + dfs_cfg->dfs_beacon_tx_enhanced = + cfg_get(psoc, CFG_DFS_BEACON_TX_ENHANCED); + dfs_cfg->sap_tx_leakage_threshold = + cfg_get(psoc, CFG_SAP_TX_LEAKAGE_THRESHOLD); } static void mlme_init_feature_flag_in_cfg( @@ -1687,6 +1703,13 @@ static void mlme_init_wep_cfg(struct wlan_mlme_wep_cfg *wep_params) mlme_init_wep_keys(wep_params); } +static void mlme_init_wifi_pos_cfg(struct wlan_objmgr_psoc *psoc, + struct wlan_mlme_wifi_pos_cfg *wifi_pos_cfg) +{ + wifi_pos_cfg->fine_time_meas_cap = + cfg_get(psoc, CFG_FINE_TIME_MEAS_CAPABILITY); +} + #ifdef FEATURE_WLAN_ESE static void mlme_init_inactivity_intv(struct wlan_objmgr_psoc *psoc, struct wlan_mlme_wmm_params *wmm_params) @@ -1844,6 +1867,7 @@ QDF_STATUS mlme_cfg_on_psoc_enable(struct wlan_objmgr_psoc *psoc) mlme_init_power_cfg(psoc, &mlme_cfg->power); mlme_init_oce_cfg(psoc, &mlme_cfg->oce); mlme_init_wep_cfg(&mlme_cfg->wep_params); + mlme_init_wifi_pos_cfg(psoc, &mlme_cfg->wifi_pos_cfg); mlme_init_wps_params_cfg(psoc, &mlme_cfg->wps_params); return status; diff --git a/components/mlme/dispatcher/inc/cfg_mlme.h b/components/mlme/dispatcher/inc/cfg_mlme.h index 3e95ad392c99..8b8292bb6471 100644 --- a/components/mlme/dispatcher/inc/cfg_mlme.h +++ b/components/mlme/dispatcher/inc/cfg_mlme.h @@ -50,6 +50,7 @@ #include "cfg_mlme_threshold.h" #include "cfg_mlme_feature_flag.h" #include "cfg_mlme_wep_params.h" +#include "cfg_mlme_wifi_pos.h" /* Please Maintain Alphabetic Order here */ #define CFG_MLME_ALL \ @@ -80,6 +81,7 @@ CFG_TWT_ALL \ CFG_VHT_CAPS_ALL \ CFG_WEP_PARAMS_ALL \ + CFG_WIFI_POS_ALL \ CFG_WPS_ALL #endif /* __CFG_MLME_H */ diff --git a/components/mlme/dispatcher/inc/cfg_mlme_dfs.h b/components/mlme/dispatcher/inc/cfg_mlme_dfs.h index e3dd2c583825..0a5ce30c5d1b 100644 --- a/components/mlme/dispatcher/inc/cfg_mlme_dfs.h +++ b/components/mlme/dispatcher/inc/cfg_mlme_dfs.h @@ -23,6 +23,136 @@ #ifndef __CFG_MLME_DFS_H #define __CFG_MLME_DFS_H +/* + * + * gsap_tx_leakage_threshold - sap tx leakage threshold + * @Min: 100 + * @Max: 1000 + * @Default: 310 + * + * customer can set this value from 100 to 1000 which means + * sap tx leakage threshold is -10db to -100db + * + * Related: none + * + * Usage: External + * + * + */ +#define CFG_SAP_TX_LEAKAGE_THRESHOLD CFG_INI_UINT( \ + "gsap_tx_leakage_threshold", \ + 100, \ + 1000, \ + 310, \ + CFG_VALUE_OR_DEFAULT, \ + "sap tx leakage threshold") + +/* + * + * gDfsBeaconTxEnhanced - beacon tx enhanced + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enhance dfs beacon tx + * + * Related: none + * + * Usage: External + * + * + */ +#define CFG_DFS_BEACON_TX_ENHANCED CFG_INI_BOOL( \ + "gDfsBeaconTxEnhanced", \ + 0, \ + "beacon tx enhanced") + +/* + * + * gPreferNonDfsChanOnRadar - During random channel selection prefer non dfs + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * During random channel selection prefer non dfs. + * + * Related: none + * + * Usage: External + * + * + */ +#define CFG_ENABLE_NON_DFS_CHAN_ON_RADAR CFG_INI_BOOL( \ + "gPreferNonDfsChanOnRadar", \ + 0, \ + "channel selection prefer non dfs") + +/* + * + * dfsPhyerrFilterOffload - Enable dfs phyerror filtering offload in FW + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to to enable dfs phyerror filtering offload to firmware + * Enabling it will cause basic phy error to be discarding in firmware. + * Related: NA. + * + * Supported Feature: DFS + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_DFS_PHYERR_FILTEROFFLOAD CFG_INI_BOOL( \ + "dfsPhyerrFilterOffload", \ + 0, \ + "dfs phyerror filtering offload") + +/* + * + * gIgnoreCAC - Used to ignore CAC + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to set default CAC + * + * Related: None + * + * Supported Feature: DFS + * + * Usage: Internal/External + * + * + */ +#define CFG_IGNORE_CAC CFG_INI_BOOL( \ + "gIgnoreCAC", \ + 0, \ + "ignore CAC on DFS channel") + +/* + * + * gDisableDFSChSwitch - Disable channel switch if radar is found + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to disable channel switch if radar is found + * on that channel. + * Related: NA. + * + * Supported Feature: DFS + * + * Usage: Internal + * + * + */ +#define CFG_DISABLE_DFS_CH_SWITCH CFG_INI_BOOL( \ + "gDisableDFSChSwitch", \ + 0, \ + "Disable channel switch on radar") + /* * * gEnableDFSMasterCap - Enable DFS master capability @@ -47,6 +177,12 @@ "DFS master mode capability") #define CFG_DFS_ALL \ - CFG(CFG_ENABLE_DFS_MASTER_CAPABILITY) + CFG(CFG_IGNORE_CAC) \ + CFG(CFG_DISABLE_DFS_CH_SWITCH) \ + CFG(CFG_DFS_BEACON_TX_ENHANCED) \ + CFG(CFG_SAP_TX_LEAKAGE_THRESHOLD) \ + CFG(CFG_ENABLE_NON_DFS_CHAN_ON_RADAR) \ + CFG(CFG_ENABLE_DFS_MASTER_CAPABILITY) \ + CFG(CFG_ENABLE_DFS_PHYERR_FILTEROFFLOAD) #endif /* __CFG_MLME_DFS_H */ diff --git a/components/mlme/dispatcher/inc/cfg_mlme_timeout.h b/components/mlme/dispatcher/inc/cfg_mlme_timeout.h index 7caed90aa9e3..0c23fe357586 100644 --- a/components/mlme/dispatcher/inc/cfg_mlme_timeout.h +++ b/components/mlme/dispatcher/inc/cfg_mlme_timeout.h @@ -285,6 +285,32 @@ CFG_VALUE_OR_DEFAULT, \ "PS data inactivity timeout") +/* + * + * wmi_wq_watchdog - Sets timeout period for wmi watchdog bite. + * @Min: 0 + * @Max: 30 + * @Default: 20 + * + * This ini is used to set timeout period for wmi watchdog bite. If it is + * 0 then wmi watchdog bite is disabled. + * + * Related: None + * + * Supported Feature: STA + * + * Usage: External + * + * + */ +#define CFG_WMI_WQ_WATCHDOG CFG_INI_UINT( \ + "wmi_wq_watchdog", \ + 0, \ + 30, \ + 20, \ + CFG_VALUE_OR_DEFAULT, \ + "timeout period for wmi watchdog bite") + #define CFG_TIMEOUT_ALL \ CFG(CFG_JOIN_FAILURE_TIMEOUT) \ CFG(CFG_AUTH_FAILURE_TIMEOUT) \ @@ -297,6 +323,7 @@ CFG(CFG_HEART_BEAT_THRESHOLD) \ CFG(CFG_AP_KEEP_ALIVE_TIMEOUT) \ CFG(CFG_AP_LINK_MONITOR_TIMEOUT) \ + CFG(CFG_WMI_WQ_WATCHDOG) \ CFG(CFG_PS_DATA_INACTIVITY_TIMEOUT) #endif /* __CFG_MLME_TIMEOUT_H */ diff --git a/components/mlme/dispatcher/inc/cfg_mlme_wifi_pos.h b/components/mlme/dispatcher/inc/cfg_mlme_wifi_pos.h new file mode 100644 index 000000000000..7904e8dee402 --- /dev/null +++ b/components/mlme/dispatcher/inc/cfg_mlme_wifi_pos.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains centralized definitions of converged configuration. + */ + +#ifndef __CFG_MLME_WIFI_POS_H +#define __CFG_MLME_WIFI_POS_H + +/* + * + * gfine_time_meas_cap - fine timing measurement capability information + * @Min: 0x0000 + * @Max: 0x00BD + * @Default: 0x00BD + * + * fine timing measurement capability information + * + * <----- fine_time_meas_cap (in bits) -----> + * +----------+-----+-----+------+------+-------+-------+-----+-----+ + * | 8-31 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * +----------+-----+-----+------+------+-------+-------+-----+-----+ + * | reserved | SAP | SAP |P2P-GO|P2P-GO|P2P-CLI|P2P-CLI| STA | STA | + * | |resp |init |resp |init |resp |init |resp |init | + * +----------+-----+-----+------+------+-------+-------+-----+-----+ + * + * resp - responder role; init- initiator role + * + * CFG_FINE_TIME_MEAS_CAPABILITY_MAX computed based on the table + * +-----------------+-----------------+-----------+ + * | Device Role | Initiator | Responder | + * +-----------------+-----------------+-----------+ + * | Station | Y | N | + * | P2P-CLI | Y | Y | + * | P2P-GO | Y | Y | + * | SAP | N | Y | + * +-----------------+-----------------+-----------+ + * + * Related: None + * + * Supported Feature: WIFI POS + * + * Usage: Internal/External + * + * + */ +#define CFG_FINE_TIME_MEAS_CAPABILITY CFG_INI_UINT( \ + "gfine_time_meas_cap", \ + 0x0000, \ + 0x00BD, \ + 0x00BD, \ + CFG_VALUE_OR_DEFAULT, \ + "fine timing measurement capability") + +#define CFG_WIFI_POS_ALL \ + CFG(CFG_FINE_TIME_MEAS_CAPABILITY) + +#endif /* __CFG_MLME_WIFI_POS_H */ diff --git a/components/mlme/dispatcher/inc/wlan_mlme_public_struct.h b/components/mlme/dispatcher/inc/wlan_mlme_public_struct.h index a433c97c5f09..3a59cd7fa9a2 100644 --- a/components/mlme/dispatcher/inc/wlan_mlme_public_struct.h +++ b/components/mlme/dispatcher/inc/wlan_mlme_public_struct.h @@ -573,9 +573,21 @@ struct wlan_mlme_cfg_sap { /** * struct wlan_mlme_dfs_cfg - DFS Capabilities related config items * @dfs_master_capable: Is DFS master mode support enabled + * @dfs_disable_channel_switch: disable channel switch on radar detection + * @dfs_ignore_cac: Disable cac + * @dfs_filter_offload: dfs filter offloaad + * @dfs_beacon_tx_enhanced: enhance dfs beacon tx + * @dfs_prefer_non_dfs: perefer non dfs channel after radar + * @sap_tx_leakage_threshold: sap tx leakage threshold */ struct wlan_mlme_dfs_cfg { bool dfs_master_capable; + bool dfs_disable_channel_switch; + bool dfs_ignore_cac; + bool dfs_filter_offload; + bool dfs_beacon_tx_enhanced; + bool dfs_prefer_non_dfs; + uint32_t sap_tx_leakage_threshold; }; /** @@ -1556,6 +1568,7 @@ struct wlan_mlme_power { * @ap_keep_alive_timeout: AP keep alive timeout value * @ap_link_monitor_timeout: AP link monitor timeout value * @ps_data_inactivity_timeout: PS data inactivity timeout + * @wmi_wq_watchdog_timeout: timeout period for wmi watchdog bite */ struct wlan_mlme_timeout { uint32_t join_failure_timeout; @@ -1570,6 +1583,7 @@ struct wlan_mlme_timeout { uint32_t ap_keep_alive_timeout; uint32_t ap_link_monitor_timeout; uint32_t ps_data_inactivity_timeout; + uint32_t wmi_wq_watchdog_timeout; }; /** @@ -1630,6 +1644,14 @@ struct wlan_mlme_wep_cfg { struct mlme_cfg_str wep_default_key_4; }; +/** + * struct wlan_mlme_wifi_pos_cfg - WIFI POS configs + * @fine_time_meas_cap: fine timing measurement capability information + */ +struct wlan_mlme_wifi_pos_cfg { + uint32_t fine_time_meas_cap; +}; + /** * struct wlan_mlme_cfg - MLME config items * @chainmask_cfg: VHT chainmask related cfg items @@ -1658,6 +1680,7 @@ struct wlan_mlme_wep_cfg { * @acs: ACS related CFG items * @feature_flags: Feature flag config items * @wep_params: WEP related config items + * @wifi_pos_cfg: WIFI POS config * @wmm_params: WMM related CFG & INI Items * @wps_params: WPS related CFG itmes */ @@ -1690,6 +1713,7 @@ struct wlan_mlme_cfg { struct wlan_mlme_acs acs; struct wlan_mlme_feature_flag feature_flags; struct wlan_mlme_wep_cfg wep_params; + struct wlan_mlme_wifi_pos_cfg wifi_pos_cfg; struct wlan_mlme_wmm_params wmm_params; struct wlan_mlme_wps_params wps_params; }; diff --git a/components/mlme/dispatcher/inc/wlan_mlme_ucfg_api.h b/components/mlme/dispatcher/inc/wlan_mlme_ucfg_api.h index 2dc36010df60..908d9bf768c6 100644 --- a/components/mlme/dispatcher/inc/wlan_mlme_ucfg_api.h +++ b/components/mlme/dispatcher/inc/wlan_mlme_ucfg_api.h @@ -702,6 +702,115 @@ QDF_STATUS ucfg_mlme_get_dfs_master_capability(struct wlan_objmgr_psoc *psoc, bool *val); +/* + * ucfg_mlme_get_dfs_disable_channel_switch() - Get the dfs channel switch + * @psoc: pointer to psoc object + * @dfs_disable_channel_switch: Pointer to the value which will be filled + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_dfs_disable_channel_switch(struct wlan_objmgr_psoc *psoc, + bool *dfs_disable_channel_switch); + +/* + * ucfg_mlme_set_dfs_disable_channel_switch() - Set the dfs channel switch + * @psoc: pointer to psoc object + * @dfs_disable_channel_switch: Value that needs to be set. + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_dfs_disable_channel_switch(struct wlan_objmgr_psoc *psoc, + bool dfs_disable_channel_switch); +/* + * ucfg_mlme_get_dfs_ignore_cac() - GSet the dfs ignore cac + * @psoc: pointer to psoc object + * @dfs_ignore_cac: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_dfs_ignore_cac(struct wlan_objmgr_psoc *psoc, + bool *dfs_ignore_cac); + +/* + * ucfg_mlme_set_dfs_ignore_cac() - Set the dfs ignore cac + * @psoc: pointer to psoc object + * @dfs_ignore_cac: Value that needs to be set. + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_dfs_ignore_cac(struct wlan_objmgr_psoc *psoc, + bool dfs_ignore_cac); + +/* + * ucfg_mlme_get_sap_tx_leakage_threshold() - Get sap tx leakage threshold + * @psoc: pointer to psoc object + * @sap_tx_leakage_threshold: Pointer to the value which will be filled + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_sap_tx_leakage_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t *sap_tx_leakage_threshold); + +/* + * ucfg_mlme_set_sap_tx_leakage_threshold() - Set sap tx leakage threshold + * @psoc: pointer to psoc object + * @sap_tx_leakage_threshold: Value that needs to be set. + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_sap_tx_leakage_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t sap_tx_leakage_threshold); + +/* + * ucfg_mlme_get_dfs_filter_offload() - Get the dfs filter offload + * @psoc: pointer to psoc object + * @dfs_filter_offload: Pointer to the value which will be filled + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_dfs_filter_offload(struct wlan_objmgr_psoc *psoc, + bool *dfs_filter_offload); + +/* + * ucfg_mlme_set_dfs_filter_offload() - Set the dfs filter offload + * @psoc: pointer to psoc object + * @dfs_filter_offload: Value that needs to be set. + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_dfs_filter_offload(struct wlan_objmgr_psoc *psoc, + bool dfs_filter_offload); + +/** + * ucfg_mlme_get_fine_time_meas_cap() - Get fine timing measurement capability + * @psoc: pointer to psoc object + * @fine_time_meas_cap: Pointer to the value which will be filled for the caller + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_fine_time_meas_cap(struct wlan_objmgr_psoc *psoc, + uint32_t *fine_time_meas_cap); + +/** + * ucfg_mlme_set_fine_time_meas_cap() - Set fine timing measurement capability + * @psoc: pointer to psoc object + * @fine_time_meas_cap: Value to be set + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_fine_time_meas_cap(struct wlan_objmgr_psoc *psoc, + uint32_t fine_time_meas_cap); + /** * ucfg_mlme_get_pmkid_modes() - Get PMKID modes * @psoc: pointer to psoc object @@ -1116,6 +1225,28 @@ QDF_STATUS ucfg_mlme_set_fast_roam_in_concurrency_enabled(struct wlan_objmgr_psoc *psoc, bool val); +/** + * ucfg_mlme_get_wmi_wq_watchdog_timeout() - Get timeout for wmi watchdog bite + * @psoc: pointer to psoc object + * @wmi_wq_watchdog_timeout: buffer to hold value + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_get_wmi_wq_watchdog_timeout(struct wlan_objmgr_psoc *psoc, + uint32_t *wmi_wq_watchdog_timeout); + +/** + * ucfg_mlme_set_wmi_wq_watchdog_timeout() - Set timeout for wmi watchdog bite + * @psoc: pointer to psoc object + * @wmi_wq_watchdog_timeout: value to be set + * + * Return: QDF Status + */ +QDF_STATUS +ucfg_mlme_set_wmi_wq_watchdog_timeout(struct wlan_objmgr_psoc *psoc, + uint32_t wmi_wq_watchdog_timeout); + /** * ucfg_mlme_get_ps_data_inactivity_timeout() - Get data inactivity timeout * @psoc: pointer to psoc object diff --git a/components/mlme/dispatcher/src/wlan_mlme_ucfg_api.c b/components/mlme/dispatcher/src/wlan_mlme_ucfg_api.c index 9ec2246a36d3..533e045dca38 100644 --- a/components/mlme/dispatcher/src/wlan_mlme_ucfg_api.c +++ b/components/mlme/dispatcher/src/wlan_mlme_ucfg_api.c @@ -206,6 +206,186 @@ ucfg_mlme_get_dfs_master_capability(struct wlan_objmgr_psoc *psoc, return QDF_STATUS_SUCCESS; } +QDF_STATUS +ucfg_mlme_get_fine_time_meas_cap(struct wlan_objmgr_psoc *psoc, + uint32_t *fine_time_meas_cap) +{ + struct wlan_mlme_psoc_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_obj(psoc); + if (!mlme_obj) { + *fine_time_meas_cap = + cfg_default(CFG_FINE_TIME_MEAS_CAPABILITY); + return QDF_STATUS_E_INVAL; + } + + *fine_time_meas_cap = mlme_obj->cfg.wifi_pos_cfg.fine_time_meas_cap; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_fine_time_meas_cap(struct wlan_objmgr_psoc *psoc, + uint32_t fine_time_meas_cap) +{ + struct wlan_mlme_psoc_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_obj(psoc); + if (!mlme_obj) + return QDF_STATUS_E_INVAL; + + mlme_obj->cfg.wifi_pos_cfg.fine_time_meas_cap = fine_time_meas_cap; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_dfs_disable_channel_switch(struct wlan_objmgr_psoc *psoc, + bool *dfs_disable_channel_switch) +{ + struct wlan_mlme_psoc_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_obj(psoc); + if (!mlme_obj) { + *dfs_disable_channel_switch = + cfg_default(CFG_DISABLE_DFS_CH_SWITCH); + mlme_err("mlme obj null"); + return QDF_STATUS_E_INVAL; + } + + *dfs_disable_channel_switch = + mlme_obj->cfg.dfs_cfg.dfs_disable_channel_switch; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_dfs_disable_channel_switch(struct wlan_objmgr_psoc *psoc, + bool dfs_disable_channel_switch) +{ + struct wlan_mlme_psoc_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_obj(psoc); + if (!mlme_obj) { + mlme_err("mlme obj null"); + return QDF_STATUS_E_INVAL; + } + + mlme_obj->cfg.dfs_cfg.dfs_disable_channel_switch = + dfs_disable_channel_switch; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_dfs_ignore_cac(struct wlan_objmgr_psoc *psoc, + bool *dfs_ignore_cac) +{ + struct wlan_mlme_psoc_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_obj(psoc); + if (!mlme_obj) { + *dfs_ignore_cac = cfg_default(CFG_IGNORE_CAC); + mlme_err("mlme obj null"); + return QDF_STATUS_E_INVAL; + } + + *dfs_ignore_cac = mlme_obj->cfg.dfs_cfg.dfs_ignore_cac; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_dfs_ignore_cac(struct wlan_objmgr_psoc *psoc, + bool dfs_ignore_cac) +{ + struct wlan_mlme_psoc_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_obj(psoc); + if (!mlme_obj) { + mlme_err("mlme obj null"); + return QDF_STATUS_E_INVAL; + } + + mlme_obj->cfg.dfs_cfg.dfs_ignore_cac = dfs_ignore_cac; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_sap_tx_leakage_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t *sap_tx_leakage_threshold) +{ + struct wlan_mlme_psoc_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_obj(psoc); + if (!mlme_obj) { + *sap_tx_leakage_threshold = + cfg_default(CFG_SAP_TX_LEAKAGE_THRESHOLD); + mlme_err("mlme obj null"); + return QDF_STATUS_E_INVAL; + } + + *sap_tx_leakage_threshold = + mlme_obj->cfg.dfs_cfg.sap_tx_leakage_threshold; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_sap_tx_leakage_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t sap_tx_leakage_threshold) +{ + struct wlan_mlme_psoc_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_obj(psoc); + if (!mlme_obj) { + mlme_err("mlme obj null"); + return QDF_STATUS_E_INVAL; + } + + mlme_obj->cfg.dfs_cfg.sap_tx_leakage_threshold = + sap_tx_leakage_threshold; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_get_dfs_filter_offload(struct wlan_objmgr_psoc *psoc, + bool *dfs_filter_offload) +{ + struct wlan_mlme_psoc_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_obj(psoc); + if (!mlme_obj) { + *dfs_filter_offload = + cfg_default(CFG_ENABLE_DFS_PHYERR_FILTEROFFLOAD); + mlme_err("mlme obj null"); + return QDF_STATUS_E_INVAL; + } + + *dfs_filter_offload = mlme_obj->cfg.dfs_cfg.dfs_filter_offload; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_dfs_filter_offload(struct wlan_objmgr_psoc *psoc, + bool dfs_filter_offload) +{ + struct wlan_mlme_psoc_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_obj(psoc); + if (!mlme_obj) { + mlme_err("mlme obj null"); + return QDF_STATUS_E_INVAL; + } + + mlme_obj->cfg.dfs_cfg.dfs_filter_offload = dfs_filter_offload; + + return QDF_STATUS_SUCCESS; +} + QDF_STATUS ucfg_mlme_get_pmkid_modes(struct wlan_objmgr_psoc *psoc, uint32_t *val) @@ -971,6 +1151,49 @@ ucfg_mlme_get_current_mcs_set(struct wlan_objmgr_psoc *psoc, len); } +QDF_STATUS +ucfg_mlme_get_wmi_wq_watchdog_timeout(struct wlan_objmgr_psoc *psoc, + uint32_t *wmi_wq_watchdog_timeout) +{ + struct wlan_mlme_psoc_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_obj(psoc); + if (!mlme_obj) { + *wmi_wq_watchdog_timeout = cfg_default(CFG_WMI_WQ_WATCHDOG); + mlme_err("mlme obj null"); + return QDF_STATUS_E_INVAL; + } + + *wmi_wq_watchdog_timeout = + mlme_obj->cfg.timeouts.wmi_wq_watchdog_timeout; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +ucfg_mlme_set_wmi_wq_watchdog_timeout(struct wlan_objmgr_psoc *psoc, + uint32_t wmi_wq_watchdog_timeout) +{ + struct wlan_mlme_psoc_obj *mlme_obj; + + mlme_obj = mlme_get_psoc_obj(psoc); + if (!mlme_obj) { + mlme_err("mlme obj null"); + return QDF_STATUS_E_INVAL; + } + + if (!cfg_in_range(CFG_WMI_WQ_WATCHDOG, wmi_wq_watchdog_timeout)) { + mlme_err("wmi watchdog bite timeout is invalid %d", + wmi_wq_watchdog_timeout); + return QDF_STATUS_E_INVAL; + } + + mlme_obj->cfg.timeouts.wmi_wq_watchdog_timeout = + wmi_wq_watchdog_timeout; + + return QDF_STATUS_SUCCESS; +} + QDF_STATUS ucfg_mlme_get_ps_data_inactivity_timeout(struct wlan_objmgr_psoc *psoc, uint32_t *inactivity_timeout) diff --git a/components/p2p/core/src/wlan_p2p_main.c b/components/p2p/core/src/wlan_p2p_main.c new file mode 100644 index 000000000000..711542da591b --- /dev/null +++ b/components/p2p/core/src/wlan_p2p_main.c @@ -0,0 +1,1460 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains main P2P function definitions + */ + +#include +#include +#include +#include +#include +#include +#include +#include "wlan_p2p_public_struct.h" +#include "wlan_p2p_ucfg_api.h" +#include "wlan_p2p_tgt_api.h" +#include "wlan_p2p_main.h" +#include "wlan_p2p_roc.h" +#include "wlan_p2p_off_chan_tx.h" +#include "wlan_p2p_cfg.h" +#include "cfg_ucfg_api.h" + +/** + * p2p_get_cmd_type_str() - parse cmd to string + * @cmd_type: P2P cmd type + * + * This function parse P2P cmd to string. + * + * Return: command string + */ +static char *p2p_get_cmd_type_str(enum p2p_cmd_type cmd_type) +{ + switch (cmd_type) { + case P2P_ROC_REQ: + return "P2P roc request"; + case P2P_CANCEL_ROC_REQ: + return "P2P cancel roc request"; + case P2P_MGMT_TX: + return "P2P mgmt tx request"; + case P2P_MGMT_TX_CANCEL: + return "P2P cancel mgmt tx request"; + case P2P_CLEANUP_ROC: + return "P2P cleanup roc"; + case P2P_CLEANUP_TX: + return "P2P cleanup tx"; + case P2P_SET_RANDOM_MAC: + return "P2P set random mac"; + default: + return "Invalid P2P command"; + } +} + +/** + * p2p_get_event_type_str() - parase event to string + * @event_type: P2P event type + * + * This function parse P2P event to string. + * + * Return: event string + */ +static char *p2p_get_event_type_str(enum p2p_event_type event_type) +{ + switch (event_type) { + case P2P_EVENT_SCAN_EVENT: + return "P2P scan event"; + case P2P_EVENT_MGMT_TX_ACK_CNF: + return "P2P mgmt tx ack event"; + case P2P_EVENT_RX_MGMT: + return "P2P mgmt rx event"; + case P2P_EVENT_LO_STOPPED: + return "P2P lo stop event"; + case P2P_EVENT_NOA: + return "P2P noa event"; + case P2P_EVENT_ADD_MAC_RSP: + return "P2P add mac filter resp event"; + default: + return "Invalid P2P event"; + } +} + +/** + * p2p_psoc_obj_create_notification() - Function to allocate per P2P + * soc private object + * @soc: soc context + * @data: Pointer to data + * + * This function gets called from object manager when psoc is being + * created and creates p2p soc context. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_psoc_obj_create_notification( + struct wlan_objmgr_psoc *soc, void *data) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status; + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = qdf_mem_malloc(sizeof(*p2p_soc_obj)); + if (!p2p_soc_obj) { + p2p_err("Failed to allocate p2p soc private object"); + return QDF_STATUS_E_NOMEM; + } + + p2p_soc_obj->soc = soc; + + status = wlan_objmgr_psoc_component_obj_attach(soc, + WLAN_UMAC_COMP_P2P, p2p_soc_obj, + QDF_STATUS_SUCCESS); + if (status != QDF_STATUS_SUCCESS) { + qdf_mem_free(p2p_soc_obj); + p2p_err("Failed to attach p2p component, %d", status); + return status; + } + + p2p_debug("p2p soc object create successful, %pK", p2p_soc_obj); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_psoc_obj_destroy_notification() - Free soc private object + * @soc: soc context + * @data: Pointer to data + * + * This function gets called from object manager when psoc is being + * deleted and delete p2p soc context. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_psoc_obj_destroy_notification( + struct wlan_objmgr_psoc *soc, void *data) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status; + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc private object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + p2p_soc_obj->soc = NULL; + + status = wlan_objmgr_psoc_component_obj_detach(soc, + WLAN_UMAC_COMP_P2P, p2p_soc_obj); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to detach p2p component, %d", status); + return status; + } + + p2p_debug("destroy p2p soc object, %pK", p2p_soc_obj); + + qdf_mem_free(p2p_soc_obj); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_vdev_obj_create_notification() - Allocate per p2p vdev object + * @vdev: vdev context + * @data: Pointer to data + * + * This function gets called from object manager when vdev is being + * created and creates p2p vdev context. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_vdev_obj_create_notification( + struct wlan_objmgr_vdev *vdev, void *data) +{ + struct p2p_vdev_priv_obj *p2p_vdev_obj; + QDF_STATUS status; + enum QDF_OPMODE mode; + + if (!vdev) { + p2p_err("vdev context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + p2p_debug("vdev mode:%d", mode); + if (mode != QDF_P2P_GO_MODE && + mode != QDF_STA_MODE && + mode != QDF_P2P_CLIENT_MODE && + mode != QDF_P2P_DEVICE_MODE) { + p2p_debug("won't create p2p vdev private object for mode %d", + mode); + return QDF_STATUS_SUCCESS; + } + + p2p_vdev_obj = + qdf_mem_malloc(sizeof(*p2p_vdev_obj)); + if (!p2p_vdev_obj) { + p2p_err("Failed to allocate p2p vdev object"); + return QDF_STATUS_E_NOMEM; + } + + p2p_vdev_obj->vdev = vdev; + p2p_vdev_obj->noa_status = true; + p2p_vdev_obj->non_p2p_peer_count = 0; + p2p_init_random_mac_vdev(p2p_vdev_obj); + + status = wlan_objmgr_vdev_component_obj_attach(vdev, + WLAN_UMAC_COMP_P2P, p2p_vdev_obj, + QDF_STATUS_SUCCESS); + if (status != QDF_STATUS_SUCCESS) { + p2p_deinit_random_mac_vdev(p2p_vdev_obj); + qdf_mem_free(p2p_vdev_obj); + p2p_err("Failed to attach p2p component to vdev, %d", + status); + return status; + } + + p2p_debug("p2p vdev object create successful, %pK", p2p_vdev_obj); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_vdev_obj_destroy_notification() - Free per P2P vdev object + * @vdev: vdev context + * @data: Pointer to data + * + * This function gets called from object manager when vdev is being + * deleted and delete p2p vdev context. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_vdev_obj_destroy_notification( + struct wlan_objmgr_vdev *vdev, void *data) +{ + struct p2p_vdev_priv_obj *p2p_vdev_obj; + QDF_STATUS status; + enum QDF_OPMODE mode; + + if (!vdev) { + p2p_err("vdev context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + p2p_debug("vdev mode:%d", mode); + if (mode != QDF_P2P_GO_MODE && + mode != QDF_STA_MODE && + mode != QDF_P2P_CLIENT_MODE && + mode != QDF_P2P_DEVICE_MODE){ + p2p_debug("no p2p vdev private object for mode %d", mode); + return QDF_STATUS_SUCCESS; + } + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + p2p_debug("p2p vdev object is NULL"); + return QDF_STATUS_SUCCESS; + } + p2p_deinit_random_mac_vdev(p2p_vdev_obj); + + p2p_vdev_obj->vdev = NULL; + + status = wlan_objmgr_vdev_component_obj_detach(vdev, + WLAN_UMAC_COMP_P2P, p2p_vdev_obj); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to detach p2p component, %d", status); + return status; + } + + p2p_debug("destroy p2p vdev object, p2p vdev obj:%pK, noa info:%pK", + p2p_vdev_obj, p2p_vdev_obj->noa_info); + + if (p2p_vdev_obj->noa_info) + qdf_mem_free(p2p_vdev_obj->noa_info); + + qdf_mem_free(p2p_vdev_obj); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_peer_obj_create_notification() - manages peer details per vdev + * @peer: peer object + * @arg: Pointer to private argument - NULL + * + * This function gets called from object manager when peer is being + * created. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_peer_obj_create_notification( + struct wlan_objmgr_peer *peer, void *arg) +{ + struct wlan_objmgr_vdev *vdev; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + enum QDF_OPMODE mode; + enum wlan_peer_type peer_type; + + if (!peer) { + p2p_err("peer context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + vdev = wlan_peer_get_vdev(peer); + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode != QDF_P2P_GO_MODE) + return QDF_STATUS_SUCCESS; + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_P2P); + peer_type = wlan_peer_get_peer_type(peer); + if ((peer_type == WLAN_PEER_STA) && p2p_vdev_obj) { + + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode == QDF_P2P_GO_MODE) { + p2p_vdev_obj->non_p2p_peer_count++; + p2p_debug("Non P2P peer count: %d", + p2p_vdev_obj->non_p2p_peer_count); + } + } + p2p_debug("p2p peer object create successful"); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_peer_obj_destroy_notification() - clears peer details per vdev + * @peer: peer object + * @arg: Pointer to private argument - NULL + * + * This function gets called from object manager when peer is being + * destroyed. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_peer_obj_destroy_notification( + struct wlan_objmgr_peer *peer, void *arg) +{ + struct wlan_objmgr_vdev *vdev; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct wlan_objmgr_psoc *psoc; + enum QDF_OPMODE mode; + enum wlan_peer_type peer_type; + uint8_t vdev_id; + + if (!peer) { + p2p_err("peer context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + vdev = wlan_peer_get_vdev(peer); + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode != QDF_P2P_GO_MODE) + return QDF_STATUS_SUCCESS; + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_P2P); + psoc = wlan_vdev_get_psoc(vdev); + if (!p2p_vdev_obj || !psoc) { + p2p_debug("p2p_vdev_obj:%pK psoc:%pK", p2p_vdev_obj, psoc); + return QDF_STATUS_E_INVAL; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + + peer_type = wlan_peer_get_peer_type(peer); + + if ((peer_type == WLAN_PEER_STA) && (mode == QDF_P2P_GO_MODE)) { + + p2p_vdev_obj->non_p2p_peer_count--; + + if (!p2p_vdev_obj->non_p2p_peer_count && + (p2p_vdev_obj->noa_status == false)) { + + vdev_id = wlan_vdev_get_id(vdev); + + if (ucfg_p2p_set_noa(psoc, vdev_id, + false) == QDF_STATUS_SUCCESS) + p2p_vdev_obj->noa_status = true; + else + p2p_vdev_obj->noa_status = false; + + p2p_debug("Non p2p peer disconnected from GO,NOA status: %d.", + p2p_vdev_obj->noa_status); + } + p2p_debug("Non P2P peer count: %d", + p2p_vdev_obj->non_p2p_peer_count); + } + p2p_debug("p2p peer object destroy successful"); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_send_noa_to_pe() - send noa information to pe + * @noa_info: vdev context + * + * This function sends noa information to pe since MCL layer need noa + * event. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_send_noa_to_pe(struct p2p_noa_info *noa_info) +{ + struct p2p_noa_attr *noa_attr; + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + if (!noa_info) { + p2p_err("noa info is null"); + return QDF_STATUS_E_INVAL; + } + + noa_attr = qdf_mem_malloc(sizeof(*noa_attr)); + if (!noa_attr) { + p2p_err("Failed to allocate memory for tSirP2PNoaAttr"); + return QDF_STATUS_E_NOMEM; + } + + noa_attr->index = noa_info->index; + noa_attr->opps_ps = noa_info->opps_ps; + noa_attr->ct_win = noa_info->ct_window; + if (!noa_info->num_desc) { + p2p_debug("Zero noa descriptors"); + } else { + p2p_debug("%d noa descriptors", noa_info->num_desc); + + noa_attr->noa1_count = + noa_info->noa_desc[0].type_count; + noa_attr->noa1_duration = + noa_info->noa_desc[0].duration; + noa_attr->noa1_interval = + noa_info->noa_desc[0].interval; + noa_attr->noa1_start_time = + noa_info->noa_desc[0].start_time; + if (noa_info->num_desc > 1) { + noa_attr->noa2_count = + noa_info->noa_desc[1].type_count; + noa_attr->noa2_duration = + noa_info->noa_desc[1].duration; + noa_attr->noa2_interval = + noa_info->noa_desc[1].interval; + noa_attr->noa2_start_time = + noa_info->noa_desc[1].start_time; + } + } + + p2p_debug("Sending P2P_NOA_ATTR_IND to pe"); + + msg.type = P2P_NOA_ATTR_IND; + msg.bodyval = 0; + msg.bodyptr = noa_attr; + status = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_PE, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(noa_attr); + p2p_err("post msg fail:%d", status); + } + + return status; +} + +/** + * process_peer_for_noa() - disable NoA + * @vdev: vdev object + * @psoc: soc object + * @peer: peer object + * + * This function disables NoA + * + * + * Return: QDF_STATUS + */ +static QDF_STATUS process_peer_for_noa(struct wlan_objmgr_vdev *vdev, + struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_peer *peer) +{ + struct p2p_vdev_priv_obj *p2p_vdev_obj = NULL; + enum QDF_OPMODE mode; + enum wlan_peer_type peer_type; + bool disable_noa; + uint8_t vdev_id; + + if (!vdev || !psoc || !peer) { + p2p_err("vdev:%pK psoc:%pK peer:%pK", vdev, psoc, peer); + return QDF_STATUS_E_INVAL; + } + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + p2p_err("p2p_vdev_obj:%pK", p2p_vdev_obj); + return QDF_STATUS_E_INVAL; + } + mode = wlan_vdev_mlme_get_opmode(vdev); + + peer_type = wlan_peer_get_peer_type(peer); + + disable_noa = ((mode == QDF_P2P_GO_MODE) + && p2p_vdev_obj->non_p2p_peer_count + && p2p_vdev_obj->noa_status); + + if (disable_noa && (peer_type == WLAN_PEER_STA)) { + + vdev_id = wlan_vdev_get_id(vdev); + + if (ucfg_p2p_set_noa(psoc, vdev_id, + true) == QDF_STATUS_SUCCESS) { + p2p_vdev_obj->noa_status = false; + } else { + p2p_vdev_obj->noa_status = true; + } + p2p_debug("NoA status: %d", p2p_vdev_obj->noa_status); + } + p2p_debug("process_peer_for_noa"); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_object_init_params() - init parameters for p2p object + * @psoc: pointer to psoc object + * @p2p_soc_obj: pointer to p2p psoc object + * + * This function init parameters for p2p object + */ +static QDF_STATUS p2p_object_init_params( + struct wlan_objmgr_psoc *psoc, + struct p2p_soc_priv_obj *p2p_soc_obj) +{ + if (!psoc || !p2p_soc_obj) { + p2p_err("invalid param"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj->param.go_keepalive_period = + cfg_get(psoc, CFG_GO_KEEP_ALIVE_PERIOD); + p2p_soc_obj->param.go_link_monitor_period = + cfg_get(psoc, CFG_GO_LINK_MONITOR_PERIOD); + p2p_soc_obj->param.p2p_device_addr_admin = + cfg_get(psoc, CFG_P2P_DEVICE_ADDRESS_ADMINISTRATED); + p2p_soc_obj->param.skip_dfs_channel_p2p_search = + cfg_get(psoc, CFG_ENABLE_SKIP_DFS_IN_P2P_SEARCH); + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_P2P_DEBUG +/** + * wlan_p2p_init_connection_status() - init connection status + * @p2p_soc_obj: pointer to p2p psoc object + * + * This function initial p2p connection status. + * + * Return: None + */ +static void wlan_p2p_init_connection_status( + struct p2p_soc_priv_obj *p2p_soc_obj) +{ + if (!p2p_soc_obj) { + p2p_err("invalid p2p soc obj"); + return; + } + + p2p_soc_obj->connection_status = P2P_NOT_ACTIVE; +} +#else +static void wlan_p2p_init_connection_status( + struct p2p_soc_priv_obj *p2p_soc_obj) +{ +} +#endif /* WLAN_FEATURE_P2P_DEBUG */ + +QDF_STATUS p2p_component_init(void) +{ + QDF_STATUS status; + + status = wlan_objmgr_register_psoc_create_handler( + WLAN_UMAC_COMP_P2P, + p2p_psoc_obj_create_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to register p2p obj create handler"); + goto err_reg_psoc_create; + } + + status = wlan_objmgr_register_psoc_destroy_handler( + WLAN_UMAC_COMP_P2P, + p2p_psoc_obj_destroy_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to register p2p obj delete handler"); + goto err_reg_psoc_delete; + } + + status = wlan_objmgr_register_vdev_create_handler( + WLAN_UMAC_COMP_P2P, + p2p_vdev_obj_create_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to register p2p vdev create handler"); + goto err_reg_vdev_create; + } + + status = wlan_objmgr_register_vdev_destroy_handler( + WLAN_UMAC_COMP_P2P, + p2p_vdev_obj_destroy_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to register p2p vdev delete handler"); + goto err_reg_vdev_delete; + } + + status = wlan_objmgr_register_peer_create_handler( + WLAN_UMAC_COMP_P2P, + p2p_peer_obj_create_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to register p2p peer create handler"); + goto err_reg_peer_create; + } + + status = wlan_objmgr_register_peer_destroy_handler( + WLAN_UMAC_COMP_P2P, + p2p_peer_obj_destroy_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to register p2p peer destroy handler"); + goto err_reg_peer_destroy; + } + + p2p_debug("Register p2p obj handler successful"); + + return QDF_STATUS_SUCCESS; +err_reg_peer_destroy: + wlan_objmgr_unregister_peer_create_handler(WLAN_UMAC_COMP_P2P, + p2p_peer_obj_create_notification, NULL); +err_reg_peer_create: + wlan_objmgr_unregister_vdev_destroy_handler(WLAN_UMAC_COMP_P2P, + p2p_vdev_obj_destroy_notification, NULL); +err_reg_vdev_delete: + wlan_objmgr_unregister_vdev_create_handler(WLAN_UMAC_COMP_P2P, + p2p_vdev_obj_create_notification, NULL); +err_reg_vdev_create: + wlan_objmgr_unregister_psoc_destroy_handler(WLAN_UMAC_COMP_P2P, + p2p_psoc_obj_destroy_notification, NULL); +err_reg_psoc_delete: + wlan_objmgr_unregister_psoc_create_handler(WLAN_UMAC_COMP_P2P, + p2p_psoc_obj_create_notification, NULL); +err_reg_psoc_create: + return status; +} + +QDF_STATUS p2p_component_deinit(void) +{ + QDF_STATUS status; + QDF_STATUS ret_status = QDF_STATUS_SUCCESS; + + status = wlan_objmgr_unregister_vdev_create_handler( + WLAN_UMAC_COMP_P2P, + p2p_vdev_obj_create_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to unregister p2p vdev create handler, %d", + status); + ret_status = status; + } + + status = wlan_objmgr_unregister_vdev_destroy_handler( + WLAN_UMAC_COMP_P2P, + p2p_vdev_obj_destroy_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to unregister p2p vdev delete handler, %d", + status); + ret_status = status; + } + + status = wlan_objmgr_unregister_psoc_create_handler( + WLAN_UMAC_COMP_P2P, + p2p_psoc_obj_create_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to unregister p2p obj create handler, %d", + status); + ret_status = status; + } + + status = wlan_objmgr_unregister_psoc_destroy_handler( + WLAN_UMAC_COMP_P2P, + p2p_psoc_obj_destroy_notification, + NULL); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to unregister p2p obj delete handler, %d", + status); + ret_status = status; + } + + p2p_debug("Unregister p2p obj handler complete"); + + return ret_status; +} + +QDF_STATUS p2p_psoc_object_open(struct wlan_objmgr_psoc *soc) +{ + QDF_STATUS status; + struct p2p_soc_priv_obj *p2p_soc_obj; + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc priviate object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + p2p_object_init_params(soc, p2p_soc_obj); + qdf_list_create(&p2p_soc_obj->roc_q, MAX_QUEUE_LENGTH); + qdf_list_create(&p2p_soc_obj->tx_q_roc, MAX_QUEUE_LENGTH); + qdf_list_create(&p2p_soc_obj->tx_q_ack, MAX_QUEUE_LENGTH); + + status = qdf_event_create(&p2p_soc_obj->cancel_roc_done); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to create cancel roc done event"); + goto fail_cancel_roc; + } + + status = qdf_event_create(&p2p_soc_obj->cleanup_roc_done); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to create cleanup roc done event"); + goto fail_cleanup_roc; + } + + status = qdf_event_create(&p2p_soc_obj->cleanup_tx_done); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to create cleanup roc done event"); + goto fail_cleanup_tx; + } + + qdf_runtime_lock_init(&p2p_soc_obj->roc_runtime_lock); + p2p_soc_obj->cur_roc_vdev_id = P2P_INVALID_VDEV_ID; + qdf_idr_create(&p2p_soc_obj->p2p_idr); + + p2p_debug("p2p psoc object open successful"); + + return QDF_STATUS_SUCCESS; + +fail_cleanup_tx: + qdf_event_destroy(&p2p_soc_obj->cleanup_roc_done); + +fail_cleanup_roc: + qdf_event_destroy(&p2p_soc_obj->cancel_roc_done); + +fail_cancel_roc: + qdf_list_destroy(&p2p_soc_obj->tx_q_ack); + qdf_list_destroy(&p2p_soc_obj->tx_q_roc); + qdf_list_destroy(&p2p_soc_obj->roc_q); + + return status; +} + +QDF_STATUS p2p_psoc_object_close(struct wlan_objmgr_psoc *soc) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + qdf_idr_destroy(&p2p_soc_obj->p2p_idr); + qdf_runtime_lock_deinit(&p2p_soc_obj->roc_runtime_lock); + qdf_event_destroy(&p2p_soc_obj->cleanup_tx_done); + qdf_event_destroy(&p2p_soc_obj->cleanup_roc_done); + qdf_event_destroy(&p2p_soc_obj->cancel_roc_done); + qdf_list_destroy(&p2p_soc_obj->tx_q_ack); + qdf_list_destroy(&p2p_soc_obj->tx_q_roc); + qdf_list_destroy(&p2p_soc_obj->roc_q); + + p2p_debug("p2p psoc object close successful"); + + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +static inline void p2p_init_lo_event(struct p2p_start_param *start_param, + struct p2p_start_param *req) +{ + start_param->lo_event_cb = req->lo_event_cb; + start_param->lo_event_cb_data = req->lo_event_cb_data; +} +#else +static inline void p2p_init_lo_event(struct p2p_start_param *start_param, + struct p2p_start_param *req) +{ +} +#endif + +QDF_STATUS p2p_psoc_start(struct wlan_objmgr_psoc *soc, + struct p2p_start_param *req) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_start_param *start_param; + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("P2P soc object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + start_param = qdf_mem_malloc(sizeof(*start_param)); + if (!start_param) { + p2p_err("Failed to allocate start params"); + return QDF_STATUS_E_NOMEM; + } + start_param->rx_cb = req->rx_cb; + start_param->rx_cb_data = req->rx_cb_data; + start_param->event_cb = req->event_cb; + start_param->event_cb_data = req->event_cb_data; + start_param->tx_cnf_cb = req->tx_cnf_cb; + start_param->tx_cnf_cb_data = req->tx_cnf_cb_data; + p2p_init_lo_event(start_param, req); + p2p_soc_obj->start_param = start_param; + + wlan_p2p_init_connection_status(p2p_soc_obj); + + /* register p2p lo stop and noa event */ + tgt_p2p_register_lo_ev_handler(soc); + tgt_p2p_register_noa_ev_handler(soc); + tgt_p2p_register_macaddr_rx_filter_evt_handler(soc, true); + + /* register scan request id */ + p2p_soc_obj->scan_req_id = ucfg_scan_register_requester( + soc, P2P_MODULE_NAME, tgt_p2p_scan_event_cb, + p2p_soc_obj); + + /* register rx action frame */ + p2p_mgmt_rx_action_ops(soc, true); + + p2p_debug("p2p psoc start successful, scan request id:%d", + p2p_soc_obj->scan_req_id); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_psoc_stop(struct wlan_objmgr_psoc *soc) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_start_param *start_param; + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("P2P soc object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + start_param = p2p_soc_obj->start_param; + p2p_soc_obj->start_param = NULL; + if (!start_param) { + p2p_err("start parameters is NULL"); + return QDF_STATUS_E_FAILURE; + } + + /* unregister rx action frame */ + p2p_mgmt_rx_action_ops(soc, false); + + /* clean up queue of p2p psoc private object */ + p2p_cleanup_tx_sync(p2p_soc_obj, NULL); + p2p_cleanup_roc_sync(p2p_soc_obj, NULL); + + /* unrgister scan request id*/ + ucfg_scan_unregister_requester(soc, p2p_soc_obj->scan_req_id); + + /* unregister p2p lo stop and noa event */ + tgt_p2p_register_macaddr_rx_filter_evt_handler(soc, false); + tgt_p2p_unregister_lo_ev_handler(soc); + tgt_p2p_unregister_noa_ev_handler(soc); + + start_param->rx_cb = NULL; + start_param->rx_cb_data = NULL; + start_param->event_cb = NULL; + start_param->event_cb_data = NULL; + start_param->tx_cnf_cb = NULL; + start_param->tx_cnf_cb_data = NULL; + qdf_mem_free(start_param); + + p2p_debug("p2p psoc stop successful"); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_process_cmd(struct scheduler_msg *msg) +{ + QDF_STATUS status; + + p2p_debug("msg type %d, %s", msg->type, + p2p_get_cmd_type_str(msg->type)); + + if (!(msg->bodyptr)) { + p2p_err("Invalid message body"); + return QDF_STATUS_E_INVAL; + } + switch (msg->type) { + case P2P_ROC_REQ: + status = p2p_process_roc_req( + (struct p2p_roc_context *) + msg->bodyptr); + break; + case P2P_CANCEL_ROC_REQ: + status = p2p_process_cancel_roc_req( + (struct cancel_roc_context *) + msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case P2P_MGMT_TX: + status = p2p_process_mgmt_tx( + (struct tx_action_context *) + msg->bodyptr); + break; + case P2P_MGMT_TX_CANCEL: + status = p2p_process_mgmt_tx_cancel( + (struct cancel_roc_context *) + msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case P2P_CLEANUP_ROC: + status = p2p_process_cleanup_roc_queue( + (struct p2p_cleanup_param *) + msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case P2P_CLEANUP_TX: + status = p2p_process_cleanup_tx_queue( + (struct p2p_cleanup_param *) + msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + case P2P_SET_RANDOM_MAC: + status = p2p_process_set_rand_mac(msg->bodyptr); + qdf_mem_free(msg->bodyptr); + break; + + default: + p2p_err("drop unexpected message received %d", + msg->type); + status = QDF_STATUS_E_INVAL; + break; + } + + return status; +} + +QDF_STATUS p2p_process_evt(struct scheduler_msg *msg) +{ + QDF_STATUS status; + + p2p_debug("msg type %d, %s", msg->type, + p2p_get_event_type_str(msg->type)); + + if (!(msg->bodyptr)) { + p2p_err("Invalid message body"); + return QDF_STATUS_E_INVAL; + } + + switch (msg->type) { + case P2P_EVENT_MGMT_TX_ACK_CNF: + status = p2p_process_mgmt_tx_ack_cnf( + (struct p2p_tx_conf_event *) + msg->bodyptr); + break; + case P2P_EVENT_RX_MGMT: + status = p2p_process_rx_mgmt( + (struct p2p_rx_mgmt_event *) + msg->bodyptr); + break; + case P2P_EVENT_LO_STOPPED: + status = p2p_process_lo_stop( + (struct p2p_lo_stop_event *) + msg->bodyptr); + break; + case P2P_EVENT_NOA: + status = p2p_process_noa( + (struct p2p_noa_event *) + msg->bodyptr); + break; + case P2P_EVENT_ADD_MAC_RSP: + status = p2p_process_set_rand_mac_rsp( + (struct p2p_mac_filter_rsp *) + msg->bodyptr); + break; + default: + p2p_err("Drop unexpected message received %d", + msg->type); + status = QDF_STATUS_E_INVAL; + break; + } + + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + + return status; +} + +QDF_STATUS p2p_msg_flush_callback(struct scheduler_msg *msg) +{ + struct tx_action_context *tx_action; + + if (!msg || !(msg->bodyptr)) { + p2p_err("invalid msg"); + return QDF_STATUS_E_INVAL; + } + + p2p_debug("flush msg, type:%d", msg->type); + switch (msg->type) { + case P2P_MGMT_TX: + tx_action = (struct tx_action_context *)msg->bodyptr; + qdf_mem_free(tx_action->buf); + qdf_mem_free(tx_action); + break; + default: + qdf_mem_free(msg->bodyptr); + break; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_event_flush_callback(struct scheduler_msg *msg) +{ + struct p2p_noa_event *noa_event; + struct p2p_rx_mgmt_event *rx_mgmt_event; + struct p2p_tx_conf_event *tx_conf_event; + struct p2p_lo_stop_event *lo_stop_event; + + if (!msg || !(msg->bodyptr)) { + p2p_err("invalid msg"); + return QDF_STATUS_E_INVAL; + } + + p2p_debug("flush event, type:%d", msg->type); + switch (msg->type) { + case P2P_EVENT_NOA: + noa_event = (struct p2p_noa_event *)msg->bodyptr; + qdf_mem_free(noa_event->noa_info); + qdf_mem_free(noa_event); + break; + case P2P_EVENT_RX_MGMT: + rx_mgmt_event = (struct p2p_rx_mgmt_event *)msg->bodyptr; + qdf_mem_free(rx_mgmt_event->rx_mgmt); + qdf_mem_free(rx_mgmt_event); + break; + case P2P_EVENT_MGMT_TX_ACK_CNF: + tx_conf_event = (struct p2p_tx_conf_event *)msg->bodyptr; + qdf_mem_free(tx_conf_event); + qdf_nbuf_free(tx_conf_event->nbuf); + break; + case P2P_EVENT_LO_STOPPED: + lo_stop_event = (struct p2p_lo_stop_event *)msg->bodyptr; + qdf_mem_free(lo_stop_event->lo_event); + qdf_mem_free(lo_stop_event); + break; + default: + qdf_mem_free(msg->bodyptr); + break; + } + + return QDF_STATUS_SUCCESS; +} + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +QDF_STATUS p2p_process_lo_stop( + struct p2p_lo_stop_event *lo_stop_event) +{ + struct p2p_lo_event *lo_evt; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_start_param *start_param; + + if (!lo_stop_event) { + p2p_err("invalid lo stop event"); + return QDF_STATUS_E_INVAL; + } + + lo_evt = lo_stop_event->lo_event; + if (!lo_evt) { + p2p_err("invalid lo event"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = lo_stop_event->p2p_soc_obj; + + p2p_debug("vdev_id %d, reason %d", + lo_evt->vdev_id, lo_evt->reason_code); + + if (!p2p_soc_obj || !(p2p_soc_obj->start_param)) { + p2p_err("Invalid p2p soc object or start parameters"); + qdf_mem_free(lo_evt); + return QDF_STATUS_E_INVAL; + } + start_param = p2p_soc_obj->start_param; + if (start_param->lo_event_cb) + start_param->lo_event_cb( + start_param->lo_event_cb_data, lo_evt); + else + p2p_err("Invalid p2p soc obj or hdd lo event callback"); + + qdf_mem_free(lo_evt); + + return QDF_STATUS_SUCCESS; +} +#endif + +QDF_STATUS p2p_process_noa(struct p2p_noa_event *noa_event) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct p2p_noa_info *noa_info; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + enum QDF_OPMODE mode; + + if (!noa_event) { + p2p_err("invalid noa event"); + return QDF_STATUS_E_INVAL; + } + noa_info = noa_event->noa_info; + p2p_soc_obj = noa_event->p2p_soc_obj; + psoc = p2p_soc_obj->soc; + + p2p_debug("psoc:%pK, index:%d, opps_ps:%d, ct_window:%d, num_desc:%d, vdev_id:%d", + psoc, noa_info->index, noa_info->opps_ps, + noa_info->ct_window, noa_info->num_desc, + noa_info->vdev_id); + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + noa_info->vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_err("vdev obj is NULL"); + qdf_mem_free(noa_event->noa_info); + return QDF_STATUS_E_INVAL; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + p2p_debug("vdev mode:%d", mode); + if (mode != QDF_P2P_GO_MODE) { + p2p_err("invalid p2p vdev mode:%d", mode); + status = QDF_STATUS_E_INVAL; + goto fail; + } + + /* must send noa to pe since of limitation*/ + p2p_send_noa_to_pe(noa_info); + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_P2P); + if (!(p2p_vdev_obj->noa_info)) { + p2p_vdev_obj->noa_info = + qdf_mem_malloc(sizeof(struct p2p_noa_info)); + if (!(p2p_vdev_obj->noa_info)) { + p2p_err("Failed to allocate p2p noa info"); + status = QDF_STATUS_E_NOMEM; + goto fail; + } + } + qdf_mem_copy(p2p_vdev_obj->noa_info, noa_info, + sizeof(struct p2p_noa_info)); +fail: + qdf_mem_free(noa_event->noa_info); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return status; +} + +void p2p_peer_authorized(struct wlan_objmgr_vdev *vdev, uint8_t *mac_addr) +{ + QDF_STATUS status; + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_peer *peer; + uint8_t pdev_id; + + if (!vdev) { + p2p_err("vdev:%pK", vdev); + return; + } + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + p2p_err("psoc:%pK", psoc); + return; + } + pdev_id = wlan_objmgr_pdev_get_pdev_id(wlan_vdev_get_pdev(vdev)); + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, WLAN_P2P_ID); + if (!peer) { + p2p_debug("peer info not found"); + return; + } + status = process_peer_for_noa(vdev, psoc, peer); + wlan_objmgr_peer_release_ref(peer, WLAN_P2P_ID); + + if (status != QDF_STATUS_SUCCESS) { + p2p_err("status:%u", status); + return; + } + p2p_debug("peer is authorized"); +} + +#ifdef WLAN_FEATURE_P2P_DEBUG +static struct p2p_soc_priv_obj * +get_p2p_soc_obj_by_vdev(struct wlan_objmgr_vdev *vdev) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct wlan_objmgr_psoc *soc; + + if (!vdev) { + p2p_err("vdev context passed is NULL"); + return NULL; + } + + soc = wlan_vdev_get_psoc(vdev); + if (!soc) { + p2p_err("soc context is NULL"); + return NULL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) + p2p_err("P2P soc context is NULL"); + + return p2p_soc_obj; +} + +QDF_STATUS p2p_status_scan(struct wlan_objmgr_vdev *vdev) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + enum QDF_OPMODE mode; + + p2p_soc_obj = get_p2p_soc_obj_by_vdev(vdev); + if (!p2p_soc_obj) { + p2p_err("P2P soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode != QDF_P2P_CLIENT_MODE && + mode != QDF_P2P_DEVICE_MODE) { + p2p_debug("this is not P2P CLIENT or DEVICE, mode:%d", + mode); + return QDF_STATUS_SUCCESS; + } + + p2p_debug("connection status:%d", p2p_soc_obj->connection_status); + switch (p2p_soc_obj->connection_status) { + case P2P_GO_NEG_COMPLETED: + case P2P_GO_NEG_PROCESS: + p2p_soc_obj->connection_status = + P2P_CLIENT_CONNECTING_STATE_1; + p2p_debug("[P2P State] Changing state from Go nego completed to Connection is started"); + p2p_debug("P2P Scanning is started for 8way Handshake"); + break; + case P2P_CLIENT_DISCONNECTED_STATE: + p2p_soc_obj->connection_status = + P2P_CLIENT_CONNECTING_STATE_2; + p2p_debug("[P2P State] Changing state from Disconnected state to Connection is started"); + p2p_debug("P2P Scanning is started for 4way Handshake"); + break; + default: + break; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_status_connect(struct wlan_objmgr_vdev *vdev) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + enum QDF_OPMODE mode; + + p2p_soc_obj = get_p2p_soc_obj_by_vdev(vdev); + if (!p2p_soc_obj) { + p2p_err("P2P soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode != QDF_P2P_CLIENT_MODE) { + p2p_debug("this is not P2P CLIENT, mode:%d", mode); + return QDF_STATUS_SUCCESS; + } + + p2p_debug("connection status:%d", p2p_soc_obj->connection_status); + switch (p2p_soc_obj->connection_status) { + case P2P_CLIENT_CONNECTING_STATE_1: + p2p_soc_obj->connection_status = + P2P_CLIENT_CONNECTED_STATE_1; + p2p_debug("[P2P State] Changing state from Connecting state to Connected State for 8-way Handshake"); + break; + case P2P_CLIENT_DISCONNECTED_STATE: + p2p_debug("No scan before 4-way handshake"); + /* + * Fall thru since no scan before 4-way handshake and + * won't enter state P2P_CLIENT_CONNECTING_STATE_2: + */ + case P2P_CLIENT_CONNECTING_STATE_2: + p2p_soc_obj->connection_status = + P2P_CLIENT_COMPLETED_STATE; + p2p_debug("[P2P State] Changing state from Connecting state to P2P Client Connection Completed"); + break; + default: + break; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_status_disconnect(struct wlan_objmgr_vdev *vdev) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + enum QDF_OPMODE mode; + + p2p_soc_obj = get_p2p_soc_obj_by_vdev(vdev); + if (!p2p_soc_obj) { + p2p_err("P2P soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode != QDF_P2P_CLIENT_MODE) { + p2p_debug("this is not P2P CLIENT, mode:%d", mode); + return QDF_STATUS_SUCCESS; + } + + p2p_debug("connection status:%d", p2p_soc_obj->connection_status); + switch (p2p_soc_obj->connection_status) { + case P2P_CLIENT_CONNECTED_STATE_1: + p2p_soc_obj->connection_status = + P2P_CLIENT_DISCONNECTED_STATE; + p2p_debug("[P2P State] 8 way Handshake completed and moved to disconnected state"); + break; + case P2P_CLIENT_COMPLETED_STATE: + p2p_soc_obj->connection_status = P2P_NOT_ACTIVE; + p2p_debug("[P2P State] P2P Client is removed and moved to inactive state"); + break; + default: + break; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_status_start_bss(struct wlan_objmgr_vdev *vdev) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + enum QDF_OPMODE mode; + + p2p_soc_obj = get_p2p_soc_obj_by_vdev(vdev); + if (!p2p_soc_obj) { + p2p_err("P2P soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode != QDF_P2P_GO_MODE) { + p2p_debug("this is not P2P GO, mode:%d", mode); + return QDF_STATUS_SUCCESS; + } + + p2p_debug("connection status:%d", p2p_soc_obj->connection_status); + switch (p2p_soc_obj->connection_status) { + case P2P_GO_NEG_COMPLETED: + p2p_soc_obj->connection_status = + P2P_GO_COMPLETED_STATE; + p2p_debug("[P2P State] From Go nego completed to Non-autonomous Group started"); + break; + case P2P_NOT_ACTIVE: + p2p_soc_obj->connection_status = + P2P_GO_COMPLETED_STATE; + p2p_debug("[P2P State] From Inactive to Autonomous Group started"); + break; + default: + break; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_status_stop_bss(struct wlan_objmgr_vdev *vdev) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + enum QDF_OPMODE mode; + + p2p_soc_obj = get_p2p_soc_obj_by_vdev(vdev); + if (!p2p_soc_obj) { + p2p_err("P2P soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode != QDF_P2P_GO_MODE) { + p2p_debug("this is not P2P GO, mode:%d", mode); + return QDF_STATUS_SUCCESS; + } + + p2p_debug("connection status:%d", p2p_soc_obj->connection_status); + if (p2p_soc_obj->connection_status == P2P_GO_COMPLETED_STATE) { + p2p_soc_obj->connection_status = P2P_NOT_ACTIVE; + p2p_debug("[P2P State] From GO completed to Inactive state GO got removed"); + } + + return QDF_STATUS_SUCCESS; +} +#endif /* WLAN_FEATURE_P2P_DEBUG */ diff --git a/components/p2p/core/src/wlan_p2p_main.h b/components/p2p/core/src/wlan_p2p_main.h new file mode 100644 index 000000000000..6a6632c44e3d --- /dev/null +++ b/components/p2p/core/src/wlan_p2p_main.h @@ -0,0 +1,574 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Defines main P2P functions & structures + */ + +#ifndef _WLAN_P2P_MAIN_H_ +#define _WLAN_P2P_MAIN_H_ + +#include +#include +#include +#include +#include +#include + +#define MAX_QUEUE_LENGTH 20 +#define P2P_NOA_ATTR_IND 0x1090 +#define P2P_MODULE_NAME "P2P" +#define P2P_INVALID_VDEV_ID 0xFFFFFFFF +#define MAX_RANDOM_MAC_ADDRS 4 + +#define p2p_debug(params ...) \ + QDF_TRACE_DEBUG(QDF_MODULE_ID_P2P, params) +#define p2p_info(params ...) \ + QDF_TRACE_INFO(QDF_MODULE_ID_P2P, params) +#define p2p_warn(params ...) \ + QDF_TRACE_WARN(QDF_MODULE_ID_P2P, params) +#define p2p_err(params ...) \ + QDF_TRACE_ERROR(QDF_MODULE_ID_P2P, params) +#define p2p_alert(params ...) \ + QDF_TRACE_FATAL(QDF_MODULE_ID_P2P, params) + +#define p2p_nofl_debug(params ...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_P2P, params) +#define p2p_nofl_info(params ...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_P2P, params) +#define p2p_nofl_warn(params ...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_P2P, params) +#define p2p_nofl_err(params ...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_P2P, params) +#define p2p_nofl_alert(params ...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_P2P, params) + +struct scheduler_msg; +struct p2p_tx_cnf; +struct p2p_rx_mgmt_frame; +struct p2p_lo_event; +struct p2p_start_param; +struct p2p_noa_info; +struct tx_action_context; + +/** + * enum p2p_cmd_type - P2P request type + * @P2P_ROC_REQ: P2P roc request + * @P2P_CANCEL_ROC_REQ: Cancel P2P roc request + * @P2P_MGMT_TX: P2P tx action frame request + * @P2P_MGMT_TX_CANCEL: Cancel tx action frame request + * @P2P_CLEANUP_ROC: Cleanup roc queue + * @P2P_CLEANUP_TX: Cleanup tx mgmt queue + * @P2P_SET_RANDOM_MAC: Set Random MAC addr filter request + */ +enum p2p_cmd_type { + P2P_ROC_REQ = 0, + P2P_CANCEL_ROC_REQ, + P2P_MGMT_TX, + P2P_MGMT_TX_CANCEL, + P2P_CLEANUP_ROC, + P2P_CLEANUP_TX, + P2P_SET_RANDOM_MAC, +}; + +/** + * enum p2p_event_type - P2P event type + * @P2P_EVENT_SCAN_EVENT: P2P scan event + * @P2P_EVENT_MGMT_TX_ACK_CNF: P2P mgmt tx confirm frame + * @P2P_EVENT_RX_MGMT: P2P rx mgmt frame + * @P2P_EVENT_LO_STOPPED: P2P listen offload stopped event + * @P2P_EVENT_NOA: P2P noa event + * @P2P_EVENT_ADD_MAC_RSP: Set Random MAC addr event + */ +enum p2p_event_type { + P2P_EVENT_SCAN_EVENT = 0, + P2P_EVENT_MGMT_TX_ACK_CNF, + P2P_EVENT_RX_MGMT, + P2P_EVENT_LO_STOPPED, + P2P_EVENT_NOA, + P2P_EVENT_ADD_MAC_RSP, +}; + +/** + * struct p2p_tx_conf_event - p2p tx confirm event + * @p2p_soc_obj: p2p soc private object + * @buf: buffer address + * @status: tx status + */ +struct p2p_tx_conf_event { + struct p2p_soc_priv_obj *p2p_soc_obj; + qdf_nbuf_t nbuf; + uint32_t status; +}; + +/** + * struct p2p_rx_mgmt_event - p2p rx mgmt frame event + * @p2p_soc_obj: p2p soc private object + * @rx_mgmt: p2p rx mgmt frame structure + */ +struct p2p_rx_mgmt_event { + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_rx_mgmt_frame *rx_mgmt; +}; + +/** + * struct p2p_lo_stop_event - p2p listen offload stop event + * @p2p_soc_obj: p2p soc private object + * @lo_event: p2p lo stop structure + */ +struct p2p_lo_stop_event { + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_lo_event *lo_event; +}; + +/** + * struct p2p_noa_event - p2p noa event + * @p2p_soc_obj: p2p soc private object + * @noa_info: p2p noa information structure + */ +struct p2p_noa_event { + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_noa_info *noa_info; +}; + +/** + * struct p2p_mac_filter_rsp - p2p set mac filter respone + * @p2p_soc_obj: p2p soc private object + * @vdev_id: vdev id + * @status: successfully(1) or not (0) + */ +struct p2p_mac_filter_rsp { + struct p2p_soc_priv_obj *p2p_soc_obj; + uint32_t vdev_id; + uint32_t status; +}; + +#ifdef WLAN_FEATURE_P2P_DEBUG +/** + * enum p2p_connection_status - p2p connection status + * @P2P_NOT_ACTIVE: P2P not active status + * @P2P_GO_NEG_PROCESS: P2P GO negotiation in process + * @P2P_GO_NEG_COMPLETED: P2P GO negotiation complete + * @P2P_CLIENT_CONNECTING_STATE_1: P2P client connecting state 1 + * @P2P_GO_COMPLETED_STATE: P2P GO complete state + * @P2P_CLIENT_CONNECTED_STATE_1: P2P client connected state 1 + * @P2P_CLIENT_DISCONNECTED_STATE: P2P client disconnected state + * @P2P_CLIENT_CONNECTING_STATE_2: P2P client connecting state 2 + * @P2P_CLIENT_COMPLETED_STATE: P2P client complete state + */ +enum p2p_connection_status { + P2P_NOT_ACTIVE, + P2P_GO_NEG_PROCESS, + P2P_GO_NEG_COMPLETED, + P2P_CLIENT_CONNECTING_STATE_1, + P2P_GO_COMPLETED_STATE, + P2P_CLIENT_CONNECTED_STATE_1, + P2P_CLIENT_DISCONNECTED_STATE, + P2P_CLIENT_CONNECTING_STATE_2, + P2P_CLIENT_COMPLETED_STATE +}; +#endif + +/** + * struct p2p_param - p2p parameters to be used + * @go_keepalive_period: P2P GO keep alive period + * @go_link_monitor_period: period where link is idle and + * where we send NULL frame + * @p2p_device_addr_admin: enable/disable to derive the P2P + * MAC address from the primary MAC address + * @skip_dfs_channel_p2p_search: kip DFS Channel in case of P2P Search + * @ignore_dynamic_dtim_in_p2p_mode:Ignore Dynamic Dtim in case of P2P options + */ +struct p2p_param { + uint32_t go_keepalive_period; + uint32_t go_link_monitor_period; + bool p2p_device_addr_admin; + bool skip_dfs_channel_p2p_search; + bool ignore_dynamic_dtim_in_p2p_mode; +}; + +/** + * struct p2p_soc_priv_obj - Per SoC p2p private object + * @soc: Pointer to SoC context + * @roc_q: Queue for pending roc requests + * @tx_q_roc: Queue for tx frames waiting for RoC + * @tx_q_ack: Queue for tx frames waiting for ack + * @scan_req_id: Scan requestor id + * @start_param: Start parameters, include callbacks and user + * data to HDD + * @cancel_roc_done: Cancel roc done event + * @cleanup_roc_done: Cleanup roc done event + * @cleanup_tx_done: Cleanup tx done event + * @roc_runtime_lock: Runtime lock for roc request + * @p2p_cb: Callbacks to protocol stack + * @cur_roc_vdev_id: Vdev id of current roc + * @p2p_idr: p2p idr + * @param: p2p parameters to be used + * @connection_status:Global P2P connection status + */ +struct p2p_soc_priv_obj { + struct wlan_objmgr_psoc *soc; + qdf_list_t roc_q; + qdf_list_t tx_q_roc; + qdf_list_t tx_q_ack; + wlan_scan_requester scan_req_id; + struct p2p_start_param *start_param; + qdf_event_t cancel_roc_done; + qdf_event_t cleanup_roc_done; + qdf_event_t cleanup_tx_done; + qdf_runtime_lock_t roc_runtime_lock; + struct p2p_protocol_callbacks p2p_cb; + uint32_t cur_roc_vdev_id; + qdf_idr p2p_idr; + struct p2p_param param; +#ifdef WLAN_FEATURE_P2P_DEBUG + enum p2p_connection_status connection_status; +#endif +}; + +/** + * struct action_frame_cookie - Action frame cookie item in cookie list + * @cookie_node: qdf_list_node + * @cookie: Cookie value + */ +struct action_frame_cookie { + qdf_list_node_t cookie_node; + uint64_t cookie; +}; + +/** + * struct action_frame_random_mac - Action Frame random mac addr & + * related attrs + * @p2p_vdev_obj: p2p vdev private obj ptr + * @in_use: Checks whether random mac is in use + * @addr: Contains random mac addr + * @freq: Channel frequency + * @clear_timer: timer to clear random mac filter + * @cookie_list: List of cookies tied with random mac + */ +struct action_frame_random_mac { + struct p2p_vdev_priv_obj *p2p_vdev_obj; + bool in_use; + uint8_t addr[QDF_MAC_ADDR_SIZE]; + uint32_t freq; + qdf_mc_timer_t clear_timer; + qdf_list_t cookie_list; +}; + +/** + * p2p_request_mgr_callback_t() - callback to process set mac filter result + * @result: bool + * @context: callback context. + * + * Return: void + */ +typedef void (*p2p_request_mgr_callback_t)(bool result, void *context); + +/** + * struct random_mac_priv - request private data struct + * @result: result of request. + */ +struct random_mac_priv { + bool result; +}; + +/** + * struct p2p_set_mac_filter_req - set mac addr filter cmd data structure + * @soc: soc object + * @vdev_id: vdev id + * @mac: mac address to be set + * @freq: frequency + * @set: set or clear + * @cb: callback func to be called when the request completion + * @req_cookie: cookie to be used when request completed + */ +struct p2p_set_mac_filter_req { + struct wlan_objmgr_psoc *soc; + uint32_t vdev_id; + uint8_t mac[QDF_MAC_ADDR_SIZE]; + uint32_t freq; + bool set; + p2p_request_mgr_callback_t cb; + void *req_cookie; +}; + +/** + * struct p2p_vdev_priv_obj - Per vdev p2p private object + * @vdev: Pointer to vdev context + * @noa_info: NoA information + * @noa_status: NoA status i.e. Enabled / Disabled (TRUE/FALSE) + * @non_p2p_peer_count: Number of legacy stations connected to this GO + * @random_mac_lock: lock for random_mac list + * @random_mac: active random mac filter lists + * @pending_req: pending set mac filter request. + */ +struct p2p_vdev_priv_obj { + struct wlan_objmgr_vdev *vdev; + struct p2p_noa_info *noa_info; + bool noa_status; + uint16_t non_p2p_peer_count; + + /* random address management for management action frames */ + qdf_spinlock_t random_mac_lock; + struct action_frame_random_mac random_mac[MAX_RANDOM_MAC_ADDRS]; + struct p2p_set_mac_filter_req pending_req; +}; + +/** + * struct p2p_noa_attr - p2p noa attribute + * @rsvd1: reserved bits 1 + * @opps_ps: opps ps state of the AP + * @ct_win: ct window in TUs + * @index: identifies instance of NOA su element + * @rsvd2: reserved bits 2 + * @noa1_count: interval count of noa1 + * @noa1_duration: absent period duration of noa1 + * @noa1_interval: absent period interval of noa1 + * @noa1_start_time: 32 bit tsf time of noa1 + * @rsvd3: reserved bits 3 + * @noa2_count: interval count of noa2 + * @noa2_duration: absent period duration of noa2 + * @noa2_interval: absent period interval of noa2 + * @noa2_start_time: 32 bit tsf time of noa2 + */ +struct p2p_noa_attr { + uint32_t rsvd1:16; + uint32_t ct_win:7; + uint32_t opps_ps:1; + uint32_t index:8; + uint32_t rsvd2:24; + uint32_t noa1_count:8; + uint32_t noa1_duration; + uint32_t noa1_interval; + uint32_t noa1_start_time; + uint32_t rsvd3:24; + uint32_t noa2_count:8; + uint32_t noa2_duration; + uint32_t noa2_interval; + uint32_t noa2_start_time; +}; + +/** + * p2p_component_init() - P2P component initialization + * + * This function registers psoc/vdev create/delete handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_component_init(void); + +/** + * p2p_component_deinit() - P2P component de-init + * + * This function deregisters psoc/vdev create/delete handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_component_deinit(void); + +/** + * p2p_psoc_object_open() - Open P2P component + * @soc: soc context + * + * This function initialize p2p psoc object + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_psoc_object_open(struct wlan_objmgr_psoc *soc); + +/** + * p2p_psoc_object_close() - Close P2P component + * @soc: soc context + * + * This function de-init p2p psoc object. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_psoc_object_close(struct wlan_objmgr_psoc *soc); + +/** + * p2p_psoc_start() - Start P2P component + * @soc: soc context + * @req: P2P start parameters + * + * This function sets up layer call back in p2p psoc object + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_psoc_start(struct wlan_objmgr_psoc *soc, + struct p2p_start_param *req); + +/** + * p2p_psoc_stop() - Stop P2P component + * @soc: soc context + * + * This function clears up layer call back in p2p psoc object. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_psoc_stop(struct wlan_objmgr_psoc *soc); + +/** + * p2p_process_cmd() - Process P2P messages in OS interface queue + * @msg: message information + * + * This function is main handler for P2P messages in OS interface + * queue, it gets called by message scheduler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_cmd(struct scheduler_msg *msg); + +/** + * p2p_process_evt() - Process P2P messages in target interface queue + * @msg: message information + * + * This function is main handler for P2P messages in target interface + * queue, it gets called by message scheduler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_evt(struct scheduler_msg *msg); + +/** + * p2p_msg_flush_callback() - Callback used to flush P2P messages + * @msg: message information + * + * This callback will be called when scheduler flush some of P2P messages. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_msg_flush_callback(struct scheduler_msg *msg); + +/** + * p2p_event_flush_callback() - Callback used to flush P2P events + * @msg: event information + * + * This callback will be called when scheduler flush some of P2P events. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_event_flush_callback(struct scheduler_msg *msg); + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +/** + * p2p_process_lo_stop() - Process lo stop event + * @lo_stop_event: listen offload stop event information + * + * This function handles listen offload stop event and deliver this + * event to HDD layer by registered callback. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_lo_stop( + struct p2p_lo_stop_event *lo_stop_event); +#else +static inline QDF_STATUS p2p_process_lo_stop( + struct p2p_lo_stop_event *lo_stop_event) +{ + return QDF_STATUS_SUCCESS; +} +#endif +/** + * p2p_process_noa() - Process noa event + * @noa_event: noa event information + * + * This function handles noa event and save noa information in p2p + * vdev object. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_noa(struct p2p_noa_event *noa_event); + +#ifdef WLAN_FEATURE_P2P_DEBUG +/** + * p2p_status_scan() - Update P2P connection status + * @vdev: vdev context + * + * This function updates P2P connection status when scanning + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_status_scan(struct wlan_objmgr_vdev *vdev); + +/** + * p2p_status_connect() - Update P2P connection status + * @vdev: vdev context + * + * This function updates P2P connection status when connecting. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_status_connect(struct wlan_objmgr_vdev *vdev); + +/** + * p2p_status_disconnect() - Update P2P connection status + * @vdev: vdev context + * + * This function updates P2P connection status when disconnecting. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_status_disconnect(struct wlan_objmgr_vdev *vdev); + +/** + * p2p_status_start_bss() - Update P2P connection status + * @vdev: vdev context + * + * This function updates P2P connection status when starting BSS. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_status_start_bss(struct wlan_objmgr_vdev *vdev); + +/** + * p2p_status_stop_bss() - Update P2P connection status + * @vdev: vdev context + * + * This function updates P2P connection status when stopping BSS. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_status_stop_bss(struct wlan_objmgr_vdev *vdev); +#else +static inline QDF_STATUS p2p_status_scan(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS p2p_status_connect(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS p2p_status_disconnect(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS p2p_status_start_bss(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS p2p_status_stop_bss(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* WLAN_FEATURE_P2P_DEBUG */ +#endif /* _WLAN_P2P_MAIN_H_ */ diff --git a/components/p2p/core/src/wlan_p2p_off_chan_tx.c b/components/p2p/core/src/wlan_p2p_off_chan_tx.c new file mode 100644 index 000000000000..1309ccd08df1 --- /dev/null +++ b/components/p2p/core/src/wlan_p2p_off_chan_tx.c @@ -0,0 +1,2955 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains off channel tx API definitions + */ + +#include +#include +#include +#include +#include +#include +#include "wlan_p2p_public_struct.h" +#include "wlan_p2p_tgt_api.h" +#include "wlan_p2p_ucfg_api.h" +#include "wlan_p2p_roc.h" +#include "wlan_p2p_main.h" +#include "wlan_p2p_off_chan_tx.h" +#include "wlan_osif_request_manager.h" + +/** + * p2p_psoc_get_tx_ops() - get p2p tx ops + * @psoc: psoc object + * + * This function returns p2p tx ops callbacks. + * + * Return: wlan_lmac_if_p2p_tx_ops + */ +static inline struct wlan_lmac_if_p2p_tx_ops * +p2p_psoc_get_tx_ops(struct wlan_objmgr_psoc *psoc) +{ + return &psoc->soc_cb.tx_ops.p2p; +} + +/** + * p2p_tx_context_check_valid() - check tx action context + * @tx_ctx: tx context + * + * This function check if tx action context and parameters are valid. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_tx_context_check_valid(struct tx_action_context *tx_ctx) +{ + struct wlan_objmgr_psoc *psoc; + struct p2p_soc_priv_obj *p2p_soc_obj; + + if (!tx_ctx) { + p2p_err("null tx action context"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + if (!p2p_soc_obj) { + p2p_err("null p2p soc private object"); + return QDF_STATUS_E_INVAL; + } + + psoc = p2p_soc_obj->soc; + if (!psoc) { + p2p_err("null p2p soc object"); + return QDF_STATUS_E_INVAL; + } + + if (!tx_ctx->buf) { + p2p_err("null tx buffer"); + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_vdev_check_valid() - check vdev and vdev mode + * @tx_ctx: tx context + * + * This function check if vdev and vdev mode are valid. It will drop + * probe response in sta mode. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_vdev_check_valid(struct tx_action_context *tx_ctx) +{ + enum QDF_OPMODE mode; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + psoc = p2p_soc_obj->soc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + tx_ctx->vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_err("null vdev object"); + return QDF_STATUS_E_INVAL; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + p2p_debug("vdev mode:%d", mode); + + /* drop probe response for sta, go, sap */ + if ((mode == QDF_STA_MODE || + mode == QDF_SAP_MODE || + mode == QDF_P2P_GO_MODE) && + tx_ctx->frame_info.sub_type == P2P_MGMT_PROBE_RSP) { + p2p_debug("drop probe response, mode:%d", mode); + status = QDF_STATUS_E_FAILURE; + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return status; +} + +/** + * p2p_get_p2pie_ptr() - get the pointer to p2p ie + * @ie: source ie + * @ie_len: source ie length + * + * This function finds out p2p ie by p2p oui and return the pointer. + * + * Return: pointer to p2p ie + */ +static const uint8_t *p2p_get_p2pie_ptr(const uint8_t *ie, uint16_t ie_len) +{ + return wlan_get_vendor_ie_ptr_from_oui(P2P_OUI, + P2P_OUI_SIZE, ie, ie_len); +} + +/** + * p2p_get_p2pie_from_probe_rsp() - get the pointer to p2p ie from + * probe response + * @tx_ctx: tx context + * + * This function finds out p2p ie and return the pointer if it is a + * probe response frame. + * + * Return: pointer to p2p ie + */ +static const uint8_t *p2p_get_p2pie_from_probe_rsp( + struct tx_action_context *tx_ctx) +{ + const uint8_t *ie; + const uint8_t *p2p_ie; + const uint8_t *tmp_p2p_ie = NULL; + uint16_t ie_len; + + if (tx_ctx->buf_len <= PROBE_RSP_IE_OFFSET) { + p2p_err("Invalid header len for probe response"); + return NULL; + } + + ie = tx_ctx->buf + PROBE_RSP_IE_OFFSET; + ie_len = tx_ctx->buf_len - PROBE_RSP_IE_OFFSET; + p2p_ie = p2p_get_p2pie_ptr(ie, ie_len); + while ((p2p_ie) && + (P2P_MAX_IE_LENGTH == p2p_ie[1])) { + ie_len = tx_ctx->buf_len - (p2p_ie - tx_ctx->buf); + if (ie_len > 2) { + ie = p2p_ie + P2P_MAX_IE_LENGTH + 2; + tmp_p2p_ie = p2p_get_p2pie_ptr(ie, ie_len); + } + + if (tmp_p2p_ie) { + p2p_ie = tmp_p2p_ie; + tmp_p2p_ie = NULL; + } else { + break; + } + } + + return p2p_ie; +} + +/** + * p2p_get_presence_noa_attr() - get the pointer to noa attr + * @pies: source ie + * @length: source ie length + * + * This function finds out noa attr by noa eid and return the pointer. + * + * Return: pointer to noa attr + */ +static const uint8_t *p2p_get_presence_noa_attr(const uint8_t *pies, int length) +{ + int left = length; + const uint8_t *ptr = pies; + uint8_t elem_id; + uint16_t elem_len; + + p2p_debug("pies:%pK, length:%d", pies, length); + + while (left >= 3) { + elem_id = ptr[0]; + elem_len = ((uint16_t) ptr[1]) | (ptr[2] << 8); + + left -= 3; + if (elem_len > left) { + p2p_err("****Invalid IEs, elem_len=%d left=%d*****", + elem_len, left); + return NULL; + } + if (elem_id == P2P_NOA_ATTR) + return ptr; + + left -= elem_len; + ptr += (elem_len + 3); + } + + return NULL; +} + +/** + * p2p_get_noa_attr_stream_in_mult_p2p_ies() - get the pointer to noa + * attr from multi p2p ie + * @noa_stream: noa stream + * @noa_len: noa stream length + * @overflow_len: overflow length + * + * This function finds out noa attr from multi p2p ies. + * + * Return: noa length + */ +static uint8_t p2p_get_noa_attr_stream_in_mult_p2p_ies(uint8_t *noa_stream, + uint8_t noa_len, uint8_t overflow_len) +{ + uint8_t overflow_p2p_stream[P2P_MAX_NOA_ATTR_LEN]; + + p2p_debug("noa_stream:%pK, noa_len:%d, overflow_len:%d", + noa_stream, noa_len, overflow_len); + if ((noa_len <= (P2P_MAX_NOA_ATTR_LEN + P2P_IE_HEADER_LEN)) && + (noa_len >= overflow_len) && + (overflow_len <= P2P_MAX_NOA_ATTR_LEN)) { + qdf_mem_copy(overflow_p2p_stream, + noa_stream + noa_len - overflow_len, + overflow_len); + noa_stream[noa_len - overflow_len] = + P2P_EID_VENDOR; + noa_stream[noa_len - overflow_len + 1] = + overflow_len + P2P_OUI_SIZE; + qdf_mem_copy(noa_stream + noa_len - overflow_len + 2, + P2P_OUI, P2P_OUI_SIZE); + qdf_mem_copy(noa_stream + noa_len + 2 + P2P_OUI_SIZE - + overflow_len, overflow_p2p_stream, + overflow_len); + } + + return noa_len + P2P_IE_HEADER_LEN; +} + +/** + * p2p_get_vdev_noa_info() - get vdev noa information + * @tx_ctx: tx context + * + * This function gets vdev noa information + * + * Return: pointer to noa information + */ +static struct p2p_noa_info *p2p_get_vdev_noa_info( + struct tx_action_context *tx_ctx) +{ + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + enum QDF_OPMODE mode; + struct p2p_noa_info *noa_info = NULL; + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + psoc = p2p_soc_obj->soc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + tx_ctx->vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_err("vdev obj is NULL"); + return NULL; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + p2p_debug("vdev mode:%d", mode); + if (mode != QDF_P2P_GO_MODE) { + p2p_debug("invalid p2p vdev mode:%d", mode); + goto fail; + } + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_P2P); + + if (!p2p_vdev_obj || !(p2p_vdev_obj->noa_info)) { + p2p_debug("null noa info"); + goto fail; + } + + noa_info = p2p_vdev_obj->noa_info; + +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return noa_info; +} + +/** + * p2p_get_noa_attr_stream() - get noa stream from p2p vdev object + * @tx_ctx: tx context + * @pnoa_stream: pointer to noa stream + * + * This function finds out noa stream from p2p vdev object + * + * Return: noa stream length + */ +static uint8_t p2p_get_noa_attr_stream( + struct tx_action_context *tx_ctx, uint8_t *pnoa_stream) +{ + struct p2p_noa_info *noa_info; + struct noa_descriptor *noa_desc_0; + struct noa_descriptor *noa_desc_1; + uint8_t *pbody = pnoa_stream; + uint8_t len = 0; + + noa_info = p2p_get_vdev_noa_info(tx_ctx); + if (!noa_info) { + p2p_debug("not valid noa information"); + return 0; + } + + noa_desc_0 = &(noa_info->noa_desc[0]); + noa_desc_1 = &(noa_info->noa_desc[1]); + if ((!(noa_desc_0->duration)) && + (!(noa_desc_1->duration)) && + (!noa_info->opps_ps)) { + p2p_debug("opps ps and duration are 0"); + return 0; + } + + pbody[0] = P2P_NOA_ATTR; + pbody[3] = noa_info->index; + pbody[4] = noa_info->ct_window | (noa_info->opps_ps << 7); + len = 5; + pbody += len; + + if (noa_desc_0->duration) { + *pbody = noa_desc_0->type_count; + pbody += 1; + len += 1; + + *((uint32_t *) (pbody)) = noa_desc_0->duration; + pbody += sizeof(uint32_t); + len += 4; + + *((uint32_t *) (pbody)) = noa_desc_0->interval; + pbody += sizeof(uint32_t); + len += 4; + + *((uint32_t *) (pbody)) = noa_desc_0->start_time; + pbody += sizeof(uint32_t); + len += 4; + } + + if (noa_desc_1->duration) { + *pbody = noa_desc_1->type_count; + pbody += 1; + len += 1; + + *((uint32_t *) (pbody)) = noa_desc_1->duration; + pbody += sizeof(uint32_t); + len += 4; + + *((uint32_t *) (pbody)) = noa_desc_1->interval; + pbody += sizeof(uint32_t); + len += 4; + + *((uint32_t *) (pbody)) = noa_desc_1->start_time; + pbody += sizeof(uint32_t); + len += 4; + } + + pbody = pnoa_stream + 1; + /* one byte for Attr and 2 bytes for length */ + *((uint16_t *) (pbody)) = len - 3; + + return len; +} + +/** + * p2p_update_noa_stream() - update noa stream + * @tx_ctx: tx context + * @p2p_ie: pointer to p2p ie + * @noa_attr: pointer to noa attr + * @total_len: pointer to total length of ie + * + * This function updates noa stream. + * + * Return: noa stream length + */ +static uint16_t p2p_update_noa_stream(struct tx_action_context *tx_ctx, + uint8_t *p2p_ie, const uint8_t *noa_attr, uint32_t *total_len, + uint8_t *noa_stream) +{ + uint16_t noa_len; + uint16_t overflow_len; + uint8_t orig_len; + uint32_t nbytes_copy; + uint32_t buf_len = *total_len; + + noa_len = p2p_get_noa_attr_stream(tx_ctx, noa_stream); + if (noa_len <= 0) { + p2p_debug("do not find out noa attr"); + return 0; + } + + orig_len = p2p_ie[1]; + if (noa_attr) { + noa_len = noa_attr[1] | (noa_attr[2] << 8); + orig_len -= (noa_len + 1 + 2); + buf_len -= (noa_len + 1 + 2); + p2p_ie[1] = orig_len; + } + + if ((p2p_ie[1] + noa_len) > P2P_MAX_IE_LENGTH) { + overflow_len = p2p_ie[1] + noa_len - + P2P_MAX_IE_LENGTH; + noa_len = p2p_get_noa_attr_stream_in_mult_p2p_ies( + noa_stream, noa_len, overflow_len); + p2p_ie[1] = P2P_MAX_IE_LENGTH; + } else { + /* increment the length of P2P IE */ + p2p_ie[1] += noa_len; + } + + *total_len = buf_len; + nbytes_copy = (p2p_ie + orig_len + 2) - tx_ctx->buf; + + p2p_debug("noa_len=%d orig_len=%d p2p_ie=%pK buf_len=%d nbytes copy=%d ", + noa_len, orig_len, p2p_ie, buf_len, nbytes_copy); + + return noa_len; +} + +/** + * p2p_set_ht_caps() - set ht capability + * @tx_ctx: tx context + * @num_bytes: number bytes + * + * This function sets ht capability + * + * Return: None + */ +static void p2p_set_ht_caps(struct tx_action_context *tx_ctx, + uint32_t num_bytes) +{ +} + +/** + * p2p_populate_mac_header() - update sequence number + * @tx_ctx: tx context + * + * This function updates sequence number of this mgmt frame + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_populate_mac_header( + struct tx_action_context *tx_ctx) +{ + struct wlan_seq_ctl *seq_ctl; + struct wlan_frame_hdr *wh; + struct wlan_objmgr_peer *peer; + struct wlan_objmgr_psoc *psoc; + void *mac_addr; + uint16_t seq_num; + uint8_t pdev_id; + struct wlan_objmgr_vdev *vdev; + + psoc = tx_ctx->p2p_soc_obj->soc; + + wh = (struct wlan_frame_hdr *)tx_ctx->buf; + mac_addr = wh->i_addr1; + pdev_id = wlan_get_pdev_id_from_vdev_id(psoc, tx_ctx->vdev_id, + WLAN_P2P_ID); + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, WLAN_P2P_ID); + if (!peer) { + mac_addr = wh->i_addr2; + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, + WLAN_P2P_ID); + } + if (!peer && tx_ctx->rand_mac_tx) { + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + tx_ctx->vdev_id, + WLAN_P2P_ID); + if (vdev) { + mac_addr = wlan_vdev_mlme_get_macaddr(vdev); + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, + WLAN_P2P_ID); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + } + } + if (!peer) { + p2p_err("no valid peer"); + return QDF_STATUS_E_INVAL; + } + seq_num = (uint16_t)wlan_peer_mlme_get_next_seq_num(peer); + seq_ctl = (struct wlan_seq_ctl *)(tx_ctx->buf + + WLAN_SEQ_CTL_OFFSET); + seq_ctl->seq_num_lo = (seq_num & WLAN_LOW_SEQ_NUM_MASK); + seq_ctl->seq_num_hi = ((seq_num & WLAN_HIGH_SEQ_NUM_MASK) >> + WLAN_HIGH_SEQ_NUM_OFFSET); + + wlan_objmgr_peer_release_ref(peer, WLAN_P2P_ID); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_get_frame_type_str() - parse frame type to string + * @frame_info: frame information + * + * This function parse frame type to string. + * + * Return: command string + */ +static char *p2p_get_frame_type_str(struct p2p_frame_info *frame_info) +{ + if (frame_info->type == P2P_FRAME_NOT_SUPPORT) + return "Not support frame"; + + if (frame_info->sub_type == P2P_MGMT_NOT_SUPPORT) + return "Not support sub frame"; + + switch (frame_info->sub_type) { + case P2P_MGMT_PROBE_REQ: + return "P2P roc request"; + case P2P_MGMT_PROBE_RSP: + return "P2P cancel roc request"; + case P2P_MGMT_ACTION: + break; + default: + return "Invalid P2P command"; + } + + if (frame_info->action_type == P2P_ACTION_PRESENCE_REQ) + return "P2P action presence request"; + if (frame_info->action_type == P2P_ACTION_PRESENCE_RSP) + return "P2P action presence response"; + + switch (frame_info->public_action_type) { + case P2P_PUBLIC_ACTION_NEG_REQ: + return "GO negotiation request frame"; + case P2P_PUBLIC_ACTION_NEG_RSP: + return "GO negotiation response frame"; + case P2P_PUBLIC_ACTION_NEG_CNF: + return "GO negotiation confirm frame"; + case P2P_PUBLIC_ACTION_INVIT_REQ: + return "P2P invitation request"; + case P2P_PUBLIC_ACTION_INVIT_RSP: + return "P2P invitation response"; + case P2P_PUBLIC_ACTION_DEV_DIS_REQ: + return "Device discoverability request"; + case P2P_PUBLIC_ACTION_DEV_DIS_RSP: + return "Device discoverability response"; + case P2P_PUBLIC_ACTION_PROV_DIS_REQ: + return "Provision discovery request"; + case P2P_PUBLIC_ACTION_PROV_DIS_RSP: + return "Provision discovery response"; + case P2P_PUBLIC_ACTION_GAS_INIT_REQ: + return "GAS init request"; + case P2P_PUBLIC_ACTION_GAS_INIT_RSP: + return "GAS init response"; + case P2P_PUBLIC_ACTION_GAS_COMB_REQ: + return "GAS come back request"; + case P2P_PUBLIC_ACTION_GAS_COMB_RSP: + return "GAS come back response"; + default: + return "Not support action frame"; + } +} + +/** + * p2p_init_frame_info() - init frame information structure + * @frame_info: pointer to frame information + * + * This function init frame information structure. + * + * Return: None + */ +static void p2p_init_frame_info(struct p2p_frame_info *frame_info) +{ + frame_info->type = P2P_FRAME_NOT_SUPPORT; + frame_info->sub_type = P2P_MGMT_NOT_SUPPORT; + frame_info->public_action_type = + P2P_PUBLIC_ACTION_NOT_SUPPORT; + frame_info->action_type = P2P_ACTION_NOT_SUPPORT; +} + +/** + * p2p_get_frame_info() - get frame information from packet + * @data_buf: data buffer address + * @length: buffer length + * @frame_info: frame information + * + * This function gets frame information from packet. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_get_frame_info(uint8_t *data_buf, uint32_t length, + struct p2p_frame_info *frame_info) +{ + uint8_t type; + uint8_t sub_type; + uint8_t action_type; + uint8_t *buf = data_buf; + + p2p_init_frame_info(frame_info); + + if (length < P2P_ACTION_OFFSET + 1) { + p2p_err("invalid p2p mgmt hdr len"); + return QDF_STATUS_E_INVAL; + } + + type = P2P_GET_TYPE_FRM_FC(buf[0]); + sub_type = P2P_GET_SUBTYPE_FRM_FC(buf[0]); + if (type != P2P_FRAME_MGMT) { + p2p_err("just support mgmt frame"); + return QDF_STATUS_E_FAILURE; + } + + frame_info->type = P2P_FRAME_MGMT; + if (sub_type != P2P_MGMT_PROBE_RSP && + sub_type != P2P_MGMT_ACTION) { + p2p_err("not support sub type"); + return QDF_STATUS_E_FAILURE; + } + + if (sub_type == P2P_MGMT_PROBE_RSP) { + frame_info->sub_type = P2P_MGMT_PROBE_RSP; + p2p_debug("Probe Response"); + return QDF_STATUS_SUCCESS; + } + + if (sub_type == P2P_MGMT_PROBE_REQ) { + frame_info->sub_type = P2P_MGMT_PROBE_REQ; + p2p_debug("Probe Request"); + return QDF_STATUS_SUCCESS; + } + + frame_info->sub_type = P2P_MGMT_ACTION; + buf += P2P_ACTION_OFFSET; + if (length > P2P_PUBLIC_ACTION_FRAME_TYPE_OFFSET && + buf[0] == P2P_PUBLIC_ACTION_FRAME && + buf[1] == P2P_PUBLIC_ACTION_VENDOR_SPECIFIC && + !qdf_mem_cmp(&buf[2], P2P_OUI, P2P_OUI_SIZE)) { + buf = data_buf + + P2P_PUBLIC_ACTION_FRAME_TYPE_OFFSET; + action_type = buf[0]; + if (action_type > P2P_PUBLIC_ACTION_PROV_DIS_RSP) + frame_info->public_action_type = + P2P_PUBLIC_ACTION_NOT_SUPPORT; + else + frame_info->public_action_type = action_type; + } else if (length > P2P_ACTION_FRAME_TYPE_OFFSET && + buf[0] == P2P_ACTION_VENDOR_SPECIFIC_CATEGORY && + !qdf_mem_cmp(&buf[1], P2P_OUI, P2P_OUI_SIZE)) { + buf = data_buf + + P2P_ACTION_FRAME_TYPE_OFFSET; + action_type = buf[0]; + if (action_type == P2P_ACTION_PRESENCE_REQ) + frame_info->action_type = + P2P_ACTION_PRESENCE_REQ; + if (action_type == P2P_ACTION_PRESENCE_RSP) + frame_info->action_type = + P2P_ACTION_PRESENCE_RSP; + } else { + p2p_debug("this is not vendor specific p2p action frame"); + return QDF_STATUS_SUCCESS; + } + + p2p_debug("%s", p2p_get_frame_type_str(frame_info)); + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_P2P_DEBUG +/** + * p2p_tx_update_connection_status() - Update P2P connection status + * with tx frame + * @p2p_soc_obj: P2P soc private object + * @tx_frame_info: frame information + * @mac_to: Pointer to dest MAC address + * + * This function updates P2P connection status with tx frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_tx_update_connection_status( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct p2p_frame_info *tx_frame_info, + uint8_t *mac_to) +{ + if (!p2p_soc_obj || !tx_frame_info || !mac_to) { + p2p_err("invalid p2p_soc_obj:%pK or tx_frame_info:%pK or mac_to:%pK", + p2p_soc_obj, tx_frame_info, mac_to); + return QDF_STATUS_E_INVAL; + } + + if (tx_frame_info->public_action_type != + P2P_PUBLIC_ACTION_NOT_SUPPORT) + p2p_info("%s ---> OTA to " QDF_MAC_ADDR_STR, + p2p_get_frame_type_str(tx_frame_info), + QDF_MAC_ADDR_ARRAY(mac_to)); + + if ((tx_frame_info->public_action_type == + P2P_PUBLIC_ACTION_PROV_DIS_REQ) && + (p2p_soc_obj->connection_status == P2P_NOT_ACTIVE)) { + p2p_soc_obj->connection_status = P2P_GO_NEG_PROCESS; + p2p_info("[P2P State]Inactive state to GO negotiation progress state"); + } else if ((tx_frame_info->public_action_type == + P2P_PUBLIC_ACTION_NEG_CNF) && + (p2p_soc_obj->connection_status == + P2P_GO_NEG_PROCESS)) { + p2p_soc_obj->connection_status = P2P_GO_NEG_COMPLETED; + p2p_info("[P2P State]GO nego progress to GO nego completed state"); + } + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_rx_update_connection_status() - Update P2P connection status + * with rx frame + * @p2p_soc_obj: P2P soc private object + * @rx_frame_info: frame information + * @mac_from: Pointer to source MAC address + * + * This function updates P2P connection status with rx frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_rx_update_connection_status( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct p2p_frame_info *rx_frame_info, + uint8_t *mac_from) +{ + if (!p2p_soc_obj || !rx_frame_info || !mac_from) { + p2p_err("invalid p2p_soc_obj:%pK or rx_frame_info:%pK, mac_from:%pK", + p2p_soc_obj, rx_frame_info, mac_from); + return QDF_STATUS_E_INVAL; + } + + if (rx_frame_info->public_action_type != + P2P_PUBLIC_ACTION_NOT_SUPPORT) + p2p_info("%s <--- OTA from " QDF_MAC_ADDR_STR, + p2p_get_frame_type_str(rx_frame_info), + QDF_MAC_ADDR_ARRAY(mac_from)); + + if ((rx_frame_info->public_action_type == + P2P_PUBLIC_ACTION_PROV_DIS_REQ) && + (p2p_soc_obj->connection_status == P2P_NOT_ACTIVE)) { + p2p_soc_obj->connection_status = P2P_GO_NEG_PROCESS; + p2p_info("[P2P State]Inactive state to GO negotiation progress state"); + } else if ((rx_frame_info->public_action_type == + P2P_PUBLIC_ACTION_NEG_CNF) && + (p2p_soc_obj->connection_status == + P2P_GO_NEG_PROCESS)) { + p2p_soc_obj->connection_status = P2P_GO_NEG_COMPLETED; + p2p_info("[P2P State]GO negotiation progress to GO negotiation completed state"); + } else if ((rx_frame_info->public_action_type == + P2P_PUBLIC_ACTION_INVIT_REQ) && + (p2p_soc_obj->connection_status == P2P_NOT_ACTIVE)) { + p2p_soc_obj->connection_status = P2P_GO_NEG_COMPLETED; + p2p_info("[P2P State]Inactive state to GO negotiation completed state Autonomous GO formation"); + } + + return QDF_STATUS_SUCCESS; +} +#else +static QDF_STATUS p2p_tx_update_connection_status( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct p2p_frame_info *tx_frame_info, + uint8_t *mac_to) +{ + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS p2p_rx_update_connection_status( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct p2p_frame_info *rx_frame_info, + uint8_t *mac_from) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * p2p_packet_alloc() - allocate qdf nbuf + * @size: buffe size + * @data: pointer to qdf nbuf data point + * @ppPacket: pointer to qdf nbuf point + * + * This function allocates qdf nbuf. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_packet_alloc(uint16_t size, void **data, + qdf_nbuf_t *ppPacket) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + qdf_nbuf_t nbuf; + + nbuf = qdf_nbuf_alloc(NULL, + roundup(size + P2P_TX_PKT_MIN_HEADROOM, 4), + P2P_TX_PKT_MIN_HEADROOM, sizeof(uint32_t), + false); + + if (nbuf != NULL) { + qdf_nbuf_put_tail(nbuf, size); + qdf_nbuf_set_protocol(nbuf, ETH_P_CONTROL); + *ppPacket = nbuf; + *data = qdf_nbuf_data(nbuf); + qdf_mem_set(*data, size, 0); + status = QDF_STATUS_SUCCESS; + } + + return status; +} + +/** + * p2p_send_tx_conf() - send tx confirm + * @tx_ctx: tx context + * @status: tx status + * + * This function send tx confirm to osif + * + * Return: QDF_STATUS_SUCCESS - pointer to tx context + */ +static QDF_STATUS p2p_send_tx_conf(struct tx_action_context *tx_ctx, + bool status) +{ + struct p2p_tx_cnf tx_cnf; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_start_param *start_param; + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + + if (!p2p_soc_obj || !(p2p_soc_obj->start_param)) { + p2p_err("Invalid p2p soc object or start parameters"); + return QDF_STATUS_E_INVAL; + } + + start_param = p2p_soc_obj->start_param; + if (!(start_param->tx_cnf_cb)) { + p2p_err("no tx confirm callback"); + return QDF_STATUS_E_INVAL; + } + + if (tx_ctx->no_ack) + tx_cnf.action_cookie = 0; + else + tx_cnf.action_cookie = (uint64_t)tx_ctx->id; + + tx_cnf.vdev_id = tx_ctx->vdev_id; + tx_cnf.buf = tx_ctx->buf; + tx_cnf.buf_len = tx_ctx->buf_len; + tx_cnf.status = status ? 0 : 1; + + p2p_debug("soc:%pK, vdev_id:%d, action_cookie:%llx, len:%d, status:%d, buf:%pK", + p2p_soc_obj->soc, tx_cnf.vdev_id, + tx_cnf.action_cookie, tx_cnf.buf_len, + tx_cnf.status, tx_cnf.buf); + + p2p_rand_mac_tx_done(p2p_soc_obj->soc, tx_ctx); + + start_param->tx_cnf_cb(start_param->tx_cnf_cb_data, &tx_cnf); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_mgmt_tx() - call mgmt tx api + * @tx_ctx: tx context + * @buf_len: buffer length + * @packet: pointer to qdf nbuf + * @frame: pointer to qdf nbuf data + * + * This function call mgmt tx api to tx this action frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_mgmt_tx(struct tx_action_context *tx_ctx, + uint32_t buf_len, qdf_nbuf_t packet, uint8_t *frame) +{ + QDF_STATUS status; + mgmt_tx_download_comp_cb tx_comp_cb; + mgmt_ota_comp_cb tx_ota_comp_cb; + struct wlan_frame_hdr *wh; + struct wlan_objmgr_peer *peer; + struct wmi_mgmt_params mgmt_param = { 0 }; + struct wlan_objmgr_psoc *psoc; + void *mac_addr; + uint8_t pdev_id; + struct wlan_objmgr_vdev *vdev; + + psoc = tx_ctx->p2p_soc_obj->soc; + mgmt_param.tx_frame = packet; + mgmt_param.frm_len = buf_len; + mgmt_param.vdev_id = tx_ctx->vdev_id; + mgmt_param.pdata = frame; + mgmt_param.chanfreq = wlan_chan_to_freq(tx_ctx->chan); + + mgmt_param.qdf_ctx = wlan_psoc_get_qdf_dev(psoc); + if (!(mgmt_param.qdf_ctx)) { + p2p_err("qdf ctx is null"); + return QDF_STATUS_E_INVAL; + } + + wh = (struct wlan_frame_hdr *)frame; + mac_addr = wh->i_addr1; + pdev_id = wlan_get_pdev_id_from_vdev_id(psoc, tx_ctx->vdev_id, + WLAN_P2P_ID); + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, WLAN_P2P_ID); + if (!peer) { + mac_addr = wh->i_addr2; + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, + WLAN_P2P_ID); + } + if (!peer && tx_ctx->rand_mac_tx) { + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + psoc, tx_ctx->vdev_id, WLAN_P2P_ID); + if (vdev) { + mac_addr = wlan_vdev_mlme_get_macaddr(vdev); + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, + WLAN_P2P_ID); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + } + } + + if (!peer) { + p2p_err("no valid peer"); + return QDF_STATUS_E_INVAL; + } + + if (tx_ctx->no_ack) { + tx_comp_cb = tgt_p2p_mgmt_download_comp_cb; + tx_ota_comp_cb = NULL; + } else { + tx_comp_cb = NULL; + tx_ota_comp_cb = tgt_p2p_mgmt_ota_comp_cb; + } + + p2p_debug("length:%d, vdev_id:%d, chanfreq:%d, no_ack:%d", + mgmt_param.frm_len, mgmt_param.vdev_id, + mgmt_param.chanfreq, tx_ctx->no_ack); + + tx_ctx->nbuf = packet; + + status = wlan_mgmt_txrx_mgmt_frame_tx(peer, tx_ctx->p2p_soc_obj, + packet, tx_comp_cb, tx_ota_comp_cb, + WLAN_UMAC_COMP_P2P, &mgmt_param); + + wlan_objmgr_peer_release_ref(peer, WLAN_P2P_ID); + + return status; +} + +/** + * p2p_roc_req_for_tx_action() - new a roc request for tx + * @tx_ctx: tx context + * + * This function new a roc request for tx and call roc api to process + * this new roc request. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_roc_req_for_tx_action( + struct tx_action_context *tx_ctx) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *roc_ctx; + QDF_STATUS status; + + roc_ctx = qdf_mem_malloc(sizeof(struct p2p_roc_context)); + if (!roc_ctx) { + p2p_err("Failed to allocate p2p roc context"); + return QDF_STATUS_E_NOMEM; + } + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + roc_ctx->p2p_soc_obj = p2p_soc_obj; + roc_ctx->vdev_id = tx_ctx->vdev_id; + roc_ctx->chan = tx_ctx->chan; + roc_ctx->duration = tx_ctx->duration; + roc_ctx->roc_state = ROC_STATE_IDLE; + roc_ctx->roc_type = OFF_CHANNEL_TX; + roc_ctx->tx_ctx = tx_ctx; + roc_ctx->id = tx_ctx->id; + tx_ctx->roc_cookie = (uintptr_t)roc_ctx; + + p2p_debug("create roc request for off channel tx, tx ctx:%pK, roc ctx:%pK", + tx_ctx, roc_ctx); + + status = p2p_process_roc_req(roc_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("request roc for tx action frrame fail"); + return status; + } + + status = qdf_list_insert_back(&p2p_soc_obj->tx_q_roc, + &tx_ctx->node); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to insert off chan tx context to wait roc req queue"); + + return status; +} + +/** + * p2p_find_tx_ctx() - find tx context by cookie + * @p2p_soc_obj: p2p soc object + * @cookie: cookie to this p2p tx context + * @is_roc_q: it is in waiting for roc queue + * @is_ack_q: it is in waiting for ack queue + * + * This function finds out tx context by cookie. + * + * Return: pointer to tx context + */ +static struct tx_action_context *p2p_find_tx_ctx( + struct p2p_soc_priv_obj *p2p_soc_obj, uint64_t cookie, + bool *is_roc_q, bool *is_ack_q) +{ + struct tx_action_context *cur_tx_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + *is_roc_q = false; + *is_ack_q = false; + + p2p_debug("Start to find tx ctx, p2p soc_obj:%pK, cookie:%llx", + p2p_soc_obj, cookie); + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_roc, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + cur_tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + if ((uintptr_t) cur_tx_ctx == cookie) { + *is_roc_q = true; + p2p_debug("find tx ctx, cookie:%llx", cookie); + return cur_tx_ctx; + } + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_roc, + p_node, &p_node); + } + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_ack, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + cur_tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + if ((uintptr_t) cur_tx_ctx == cookie) { + *is_ack_q = true; + p2p_debug("find tx ctx, cookie:%llx", cookie); + return cur_tx_ctx; + } + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_ack, + p_node, &p_node); + } + + return NULL; +} + +/** + * p2p_find_tx_ctx_by_roc() - find tx context by roc + * @p2p_soc_obj: p2p soc object + * @cookie: cookie to roc context + * + * This function finds out tx context by roc context. + * + * Return: pointer to tx context + */ +static struct tx_action_context *p2p_find_tx_ctx_by_roc( + struct p2p_soc_priv_obj *p2p_soc_obj, uint64_t cookie) +{ + struct tx_action_context *cur_tx_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + p2p_debug("Start to find tx ctx, p2p soc_obj:%pK, cookie:%llx", + p2p_soc_obj, cookie); + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_roc, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + cur_tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + if (cur_tx_ctx->roc_cookie == cookie) { + p2p_debug("find tx ctx, cookie:%llx", cookie); + return cur_tx_ctx; + } + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_roc, + p_node, &p_node); + } + + return NULL; +} + +/** + * p2p_move_tx_context_to_ack_queue() - move tx context to tx_q_ack + * @tx_ctx: tx context + * + * This function moves tx context to waiting for ack queue. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_move_tx_context_to_ack_queue( + struct tx_action_context *tx_ctx) +{ + bool is_roc_q = false; + bool is_ack_q = false; + struct p2p_soc_priv_obj *p2p_soc_obj = tx_ctx->p2p_soc_obj; + struct tx_action_context *cur_tx_ctx; + QDF_STATUS status; + + p2p_debug("move tx context to wait for roc queue, %pK", tx_ctx); + + cur_tx_ctx = p2p_find_tx_ctx(p2p_soc_obj, (uintptr_t)tx_ctx, + &is_roc_q, &is_ack_q); + if (cur_tx_ctx) { + if (is_roc_q) { + p2p_debug("find in wait for roc queue"); + status = qdf_list_remove_node( + &p2p_soc_obj->tx_q_roc, + (qdf_list_node_t *)tx_ctx); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to remove off chan tx context from wait roc req queue"); + } + + if (is_ack_q) { + p2p_debug("Already in waiting for ack queue"); + return QDF_STATUS_SUCCESS; + } + } + + status = qdf_list_insert_back( + &p2p_soc_obj->tx_q_ack, + &tx_ctx->node); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to insert off chan tx context to wait ack req queue"); + p2p_debug("insert tx context to wait for ack queue, status:%d", + status); + + return status; +} + +/** + * p2p_extend_roc_timer() - extend roc timer + * @p2p_soc_obj: p2p soc private object + * @frame_info: pointer to frame information + * + * This function extends roc timer for some of p2p public action frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_extend_roc_timer( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct p2p_frame_info *frame_info) +{ + struct p2p_roc_context *curr_roc_ctx; + uint32_t extend_time; + + curr_roc_ctx = p2p_find_current_roc_ctx(p2p_soc_obj); + if (!curr_roc_ctx) { + p2p_debug("no running roc request currently"); + return QDF_STATUS_SUCCESS; + } + + if (!frame_info) { + p2p_err("invalid frame information"); + return QDF_STATUS_E_INVAL; + } + + switch (frame_info->public_action_type) { + case P2P_PUBLIC_ACTION_NEG_REQ: + case P2P_PUBLIC_ACTION_NEG_RSP: + extend_time = 2 * P2P_ACTION_FRAME_DEFAULT_WAIT; + break; + case P2P_PUBLIC_ACTION_INVIT_REQ: + case P2P_PUBLIC_ACTION_DEV_DIS_REQ: + extend_time = P2P_ACTION_FRAME_DEFAULT_WAIT; + break; + default: + extend_time = 0; + break; + } + + if (extend_time) { + p2p_debug("extend roc timer, duration:%d", extend_time); + curr_roc_ctx->duration = extend_time; + return p2p_restart_roc_timer(curr_roc_ctx); + } + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_adjust_tx_wait() - adjust tx wait + * @tx_ctx: tx context + * + * This function adjust wait time of this tx context + * + * Return: None + */ +static void p2p_adjust_tx_wait(struct tx_action_context *tx_ctx) +{ + struct p2p_frame_info *frame_info; + + frame_info = &(tx_ctx->frame_info); + switch (frame_info->public_action_type) { + case P2P_PUBLIC_ACTION_NEG_RSP: + case P2P_PUBLIC_ACTION_PROV_DIS_RSP: + tx_ctx->duration += P2P_ACTION_FRAME_RSP_WAIT; + break; + case P2P_PUBLIC_ACTION_NEG_CNF: + case P2P_PUBLIC_ACTION_INVIT_RSP: + tx_ctx->duration += P2P_ACTION_FRAME_ACK_WAIT; + break; + default: + break; + } +} + +/** + * p2p_remove_tx_context() - remove tx ctx from queue + * @tx_ctx: tx context + * + * This function remove tx context from waiting for roc queue or + * waiting for ack queue. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_remove_tx_context( + struct tx_action_context *tx_ctx) +{ + bool is_roc_q = false; + bool is_ack_q = false; + struct tx_action_context *cur_tx_ctx; + uint64_t cookie = (uintptr_t)tx_ctx; + struct p2p_soc_priv_obj *p2p_soc_obj = tx_ctx->p2p_soc_obj; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_debug("tx context:%pK", tx_ctx); + + cur_tx_ctx = p2p_find_tx_ctx(p2p_soc_obj, cookie, &is_roc_q, + &is_ack_q); + + /* for not off channel tx case, won't find from queue */ + if (!cur_tx_ctx) { + p2p_debug("Do not find tx context from queue"); + goto end; + } + + if (is_roc_q) { + status = qdf_list_remove_node( + &p2p_soc_obj->tx_q_roc, + (qdf_list_node_t *)cur_tx_ctx); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to tx context from wait roc req queue"); + } + + if (is_ack_q) { + status = qdf_list_remove_node( + &p2p_soc_obj->tx_q_ack, + (qdf_list_node_t *)cur_tx_ctx); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to tx context from wait ack req queue"); + } + +end: + if (!tx_ctx->roc_cookie) + qdf_idr_remove(&p2p_soc_obj->p2p_idr, tx_ctx->id); + qdf_mem_free(tx_ctx->buf); + qdf_mem_free(tx_ctx); + + return status; +} + +/** + * p2p_tx_timeout() - Callback for tx timeout + * @pdata: pointer to tx context + * + * This function is callback for tx time out. + * + * Return: None + */ +static void p2p_tx_timeout(void *pdata) +{ + struct tx_action_context *tx_ctx = pdata; + + p2p_info("pdata:%pK", pdata); + + if (!tx_ctx || !(tx_ctx->p2p_soc_obj)) { + p2p_err("invalid tx context or p2p soc object"); + return; + } + + qdf_mc_timer_destroy(&tx_ctx->tx_timer); + p2p_send_tx_conf(tx_ctx, false); + p2p_remove_tx_context(tx_ctx); +} + +/** + * p2p_enable_tx_timer() - enable tx timer + * @tx_ctx: tx context + * + * This function enable tx timer for action frame required ota tx. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_enable_tx_timer(struct tx_action_context *tx_ctx) +{ + QDF_STATUS status; + + p2p_debug("tx context:%pK", tx_ctx); + + status = qdf_mc_timer_init(&tx_ctx->tx_timer, + QDF_TIMER_TYPE_SW, p2p_tx_timeout, + tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to init tx timer"); + return status; + } + + status = qdf_mc_timer_start(&tx_ctx->tx_timer, + P2P_ACTION_FRAME_TX_TIMEOUT); + if (status != QDF_STATUS_SUCCESS) + p2p_err("tx timer start failed"); + + return status; +} + +/** + * p2p_disable_tx_timer() - disable tx timer + * @tx_ctx: tx context + * + * This function disable tx timer for action frame required ota tx. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_disable_tx_timer(struct tx_action_context *tx_ctx) +{ + QDF_STATUS status; + + p2p_debug("tx context:%pK", tx_ctx); + + status = qdf_mc_timer_stop(&tx_ctx->tx_timer); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to stop tx timer, status:%d", status); + + status = qdf_mc_timer_destroy(&tx_ctx->tx_timer); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to destroy tx timer, status:%d", status); + + return status; +} + +/** + * is_rmf_mgmt_action_frame() - check RMF action frame by category + * @action_category: action frame actegory + * + * This function check the frame is robust mgmt action frame or not + * + * Return: true - if category is robust mgmt type + */ +static bool is_rmf_mgmt_action_frame(uint8_t action_category) +{ + switch (action_category) { + case ACTION_CATEGORY_SPECTRUM_MGMT: + case ACTION_CATEGORY_QOS: + case ACTION_CATEGORY_DLS: + case ACTION_CATEGORY_BACK: + case ACTION_CATEGORY_RRM: + case ACTION_FAST_BSS_TRNST: + case ACTION_CATEGORY_SA_QUERY: + case ACTION_CATEGORY_PROTECTED_DUAL_OF_PUBLIC_ACTION: + case ACTION_CATEGORY_WNM: + case ACTION_CATEGORY_MESH_ACTION: + case ACTION_CATEGORY_MULTIHOP_ACTION: + case ACTION_CATEGORY_DMG: + case ACTION_CATEGORY_FST: + case ACTION_CATEGORY_VENDOR_SPECIFIC_PROTECTED: + return true; + default: + break; + } + return false; +} + +/** + * p2p_populate_rmf_field() - populate unicast rmf frame + * @tx_ctx: tx_action_context + * @size: input size of frame, and output new size + * @ppbuf: input frame ptr, and output new frame + * @ppkt: input pkt, output new pkt. + * + * This function allocates new pkt for rmf frame. The + * new frame has extra space for ccmp field. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_populate_rmf_field(struct tx_action_context *tx_ctx, + uint32_t *size, uint8_t **ppbuf, qdf_nbuf_t *ppkt) +{ + struct wlan_frame_hdr *wh, *rmf_wh; + struct action_frm_hdr *action_hdr; + QDF_STATUS status = QDF_STATUS_SUCCESS; + qdf_nbuf_t pkt = NULL; + uint8_t *frame; + uint32_t frame_len; + struct p2p_soc_priv_obj *p2p_soc_obj; + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + + if (tx_ctx->frame_info.sub_type != P2P_MGMT_ACTION || + !p2p_soc_obj->p2p_cb.is_mgmt_protected) + return QDF_STATUS_SUCCESS; + if (*size < (sizeof(struct wlan_frame_hdr) + + sizeof(struct action_frm_hdr))) { + return QDF_STATUS_E_INVAL; + } + + wh = (struct wlan_frame_hdr *)(*ppbuf); + action_hdr = (struct action_frm_hdr *)(*ppbuf + sizeof(*wh)); + + if (!is_rmf_mgmt_action_frame(action_hdr->action_category)) { + p2p_debug("non rmf act frame 0x%x cat %x", + tx_ctx->frame_info.sub_type, + action_hdr->action_category); + return QDF_STATUS_SUCCESS; + } + + if (!p2p_soc_obj->p2p_cb.is_mgmt_protected( + tx_ctx->vdev_id, wh->i_addr1)) { + p2p_debug("non rmf connection vdev %d "QDF_MAC_ADDR_STR, + tx_ctx->vdev_id, QDF_MAC_ADDR_ARRAY(wh->i_addr1)); + return QDF_STATUS_SUCCESS; + } + if (!qdf_is_macaddr_group((struct qdf_mac_addr *)wh->i_addr1) && + !qdf_is_macaddr_broadcast((struct qdf_mac_addr *)wh->i_addr1)) { + + frame_len = *size + IEEE80211_CCMP_HEADERLEN + + IEEE80211_CCMP_MICLEN; + status = p2p_packet_alloc((uint16_t)frame_len, (void **)&frame, + &pkt); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to allocate %d bytes for rmf frame.", + frame_len); + return QDF_STATUS_E_NOMEM; + } + + qdf_mem_copy(frame, wh, sizeof(*wh)); + qdf_mem_copy(frame + sizeof(*wh) + IEEE80211_CCMP_HEADERLEN, + *ppbuf + sizeof(*wh), + *size - sizeof(*wh)); + rmf_wh = (struct wlan_frame_hdr *)frame; + (rmf_wh)->i_fc[1] |= IEEE80211_FC1_WEP; + p2p_debug("set protection 0x%x cat %d "QDF_MAC_ADDR_STR, + tx_ctx->frame_info.sub_type, + action_hdr->action_category, + QDF_MAC_ADDR_ARRAY(wh->i_addr1)); + + qdf_nbuf_free(*ppkt); + *ppbuf = frame; + *ppkt = pkt; + *size = frame_len; + } + + return status; +} + +/** + * p2p_execute_tx_action_frame() - execute tx action frame + * @tx_ctx: tx context + * + * This function modify p2p ie and tx this action frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_execute_tx_action_frame( + struct tx_action_context *tx_ctx) +{ + uint8_t *frame; + qdf_nbuf_t packet; + QDF_STATUS status; + uint8_t noa_len = 0; + uint8_t noa_stream[P2P_NOA_STREAM_ARR_SIZE]; + uint8_t orig_len = 0; + const uint8_t *ie; + uint8_t ie_len; + uint8_t *p2p_ie = NULL; + const uint8_t *presence_noa_attr = NULL; + uint32_t nbytes_copy; + uint32_t buf_len = tx_ctx->buf_len; + struct p2p_frame_info *frame_info; + + frame_info = &(tx_ctx->frame_info); + if (frame_info->sub_type == P2P_MGMT_PROBE_RSP) { + p2p_ie = (uint8_t *)p2p_get_p2pie_from_probe_rsp(tx_ctx); + } else if (frame_info->action_type == + P2P_ACTION_PRESENCE_RSP) { + ie = tx_ctx->buf + + P2P_PUBLIC_ACTION_FRAME_TYPE_OFFSET; + ie_len = tx_ctx->buf_len - + P2P_PUBLIC_ACTION_FRAME_TYPE_OFFSET; + p2p_ie = (uint8_t *)p2p_get_p2pie_ptr(ie, ie_len); + if (p2p_ie) { + /* extract the presence of NoA attribute inside + * P2P IE */ + ie = p2p_ie + P2P_IE_HEADER_LEN; + ie_len = p2p_ie[1]; + presence_noa_attr = p2p_get_presence_noa_attr( + ie, ie_len); + } + } + + if ((frame_info->sub_type != P2P_MGMT_NOT_SUPPORT) && + p2p_ie) { + orig_len = p2p_ie[1]; + noa_len = p2p_update_noa_stream(tx_ctx, p2p_ie, + presence_noa_attr, &buf_len, + noa_stream); + buf_len += noa_len; + } + + if (frame_info->sub_type == P2P_MGMT_PROBE_RSP) + p2p_set_ht_caps(tx_ctx, buf_len); + + /* Ok-- try to allocate some memory: */ + status = p2p_packet_alloc((uint16_t) buf_len, (void **)&frame, + &packet); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to allocate %d bytes for a Probe Request.", + buf_len); + return status; + } + + /* + * Add sequence number to action frames + * Frames are handed over in .11 format by supplicant already + */ + p2p_populate_mac_header(tx_ctx); + + if ((noa_len > 0) && p2p_ie + && (noa_len < (P2P_MAX_NOA_ATTR_LEN + + P2P_IE_HEADER_LEN))) { + /* Add 2 bytes for length and Arribute field */ + nbytes_copy = (p2p_ie + orig_len + 2) - tx_ctx->buf; + qdf_mem_copy(frame, tx_ctx->buf, nbytes_copy); + qdf_mem_copy((frame + nbytes_copy), noa_stream, + noa_len); + qdf_mem_copy((frame + nbytes_copy + noa_len), + tx_ctx->buf + nbytes_copy, + buf_len - nbytes_copy - noa_len); + } else { + qdf_mem_copy(frame, tx_ctx->buf, buf_len); + } + + status = p2p_populate_rmf_field(tx_ctx, &buf_len, &frame, &packet); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to populate rmf frame"); + qdf_nbuf_free(packet); + return status; + } + status = p2p_mgmt_tx(tx_ctx, buf_len, packet, frame); + if (status == QDF_STATUS_SUCCESS) { + if (tx_ctx->no_ack) { + p2p_send_tx_conf(tx_ctx, true); + p2p_remove_tx_context(tx_ctx); + } else { + p2p_enable_tx_timer(tx_ctx); + p2p_move_tx_context_to_ack_queue(tx_ctx); + } + } else { + p2p_err("failed to tx mgmt frame"); + qdf_nbuf_free(packet); + } + + return status; +} + +struct tx_action_context *p2p_find_tx_ctx_by_nbuf( + struct p2p_soc_priv_obj *p2p_soc_obj, void *nbuf) +{ + struct tx_action_context *cur_tx_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + if (!p2p_soc_obj) { + p2p_err("invalid p2p soc object"); + return NULL; + } + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_ack, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + cur_tx_ctx = + qdf_container_of(p_node, struct tx_action_context, node); + if (cur_tx_ctx->nbuf == nbuf) { + p2p_debug("find tx ctx, nbuf:%pK", nbuf); + status = qdf_mc_timer_stop(&cur_tx_ctx->tx_timer); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to stop tx timer, status:%d", + status); + return cur_tx_ctx; + } + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_ack, + p_node, &p_node); + } + + return NULL; +} + +void p2p_dump_tx_queue(struct p2p_soc_priv_obj *p2p_soc_obj) +{ + struct tx_action_context *tx_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + p2p_debug("dump tx queue wait for roc, p2p soc obj:%pK, size:%d", + p2p_soc_obj, qdf_list_size(&p2p_soc_obj->tx_q_roc)); + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_roc, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + p2p_debug("p2p soc object:%pK, tx ctx:%pK, vdev_id:%d, scan_id:%d, roc_cookie:%llx, chan:%d, buf:%pK, len:%d, off_chan:%d, cck:%d, ack:%d, duration:%d", + p2p_soc_obj, tx_ctx, + tx_ctx->vdev_id, tx_ctx->scan_id, + tx_ctx->roc_cookie, tx_ctx->chan, + tx_ctx->buf, tx_ctx->buf_len, + tx_ctx->off_chan, tx_ctx->no_cck, + tx_ctx->no_ack, tx_ctx->duration); + + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_roc, + p_node, &p_node); + } + + p2p_debug("dump tx queue wait for ack, size:%d", + qdf_list_size(&p2p_soc_obj->tx_q_ack)); + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_ack, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + p2p_debug("p2p soc object:%pK, tx_ctx:%pK, vdev_id:%d, scan_id:%d, roc_cookie:%llx, chan:%d, buf:%pK, len:%d, off_chan:%d, cck:%d, ack:%d, duration:%d", + p2p_soc_obj, tx_ctx, + tx_ctx->vdev_id, tx_ctx->scan_id, + tx_ctx->roc_cookie, tx_ctx->chan, + tx_ctx->buf, tx_ctx->buf_len, + tx_ctx->off_chan, tx_ctx->no_cck, + tx_ctx->no_ack, tx_ctx->duration); + + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_ack, + p_node, &p_node); + } +} + +QDF_STATUS p2p_ready_to_tx_frame(struct p2p_soc_priv_obj *p2p_soc_obj, + uint64_t cookie) +{ + struct tx_action_context *cur_tx_ctx; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + cur_tx_ctx = p2p_find_tx_ctx_by_roc(p2p_soc_obj, cookie); + + while (cur_tx_ctx) { + p2p_debug("tx_ctx:%pK", cur_tx_ctx); + status = p2p_execute_tx_action_frame(cur_tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_send_tx_conf(cur_tx_ctx, false); + p2p_remove_tx_context(cur_tx_ctx); + } + cur_tx_ctx = p2p_find_tx_ctx_by_roc(p2p_soc_obj, cookie); + } + + return status; +} + +QDF_STATUS p2p_cleanup_tx_sync( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct wlan_objmgr_vdev *vdev) +{ + struct scheduler_msg msg = {0}; + struct p2p_cleanup_param *param; + QDF_STATUS status; + uint32_t vdev_id; + + if (!p2p_soc_obj) { + p2p_err("p2p soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + p2p_debug("p2p_soc_obj:%pK, vdev:%pK", p2p_soc_obj, vdev); + param = qdf_mem_malloc(sizeof(*param)); + if (!param) { + p2p_err("failed to allocate cleanup param"); + return QDF_STATUS_E_NOMEM; + } + + param->p2p_soc_obj = p2p_soc_obj; + if (vdev) + vdev_id = (uint32_t)wlan_vdev_get_id(vdev); + else + vdev_id = P2P_INVALID_VDEV_ID; + param->vdev_id = vdev_id; + qdf_event_reset(&p2p_soc_obj->cleanup_tx_done); + msg.type = P2P_CLEANUP_TX; + msg.bodyptr = param; + msg.callback = p2p_process_cmd; + status = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, &msg); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to post message"); + qdf_mem_free(param); + return status; + } + + status = qdf_wait_single_event( + &p2p_soc_obj->cleanup_tx_done, + P2P_WAIT_CLEANUP_ROC); + + if (status != QDF_STATUS_SUCCESS) + p2p_err("wait for cleanup tx timeout, %d", status); + + return status; +} + +QDF_STATUS p2p_process_cleanup_tx_queue(struct p2p_cleanup_param *param) +{ + struct tx_action_context *curr_tx_ctx; + qdf_list_node_t *p_node; + struct p2p_soc_priv_obj *p2p_soc_obj; + uint32_t vdev_id; + QDF_STATUS status, ret; + + if (!param || !(param->p2p_soc_obj)) { + p2p_err("Invalid cleanup param"); + return QDF_STATUS_E_FAILURE; + } + + p2p_soc_obj = param->p2p_soc_obj; + vdev_id = param->vdev_id; + + p2p_debug("clean up tx queue wait for roc, size:%d, vdev_id:%d", + qdf_list_size(&p2p_soc_obj->tx_q_roc), vdev_id); + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_roc, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + curr_tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_roc, + p_node, &p_node); + if ((vdev_id == P2P_INVALID_VDEV_ID) || + (vdev_id == curr_tx_ctx->vdev_id)) { + ret = qdf_list_remove_node(&p2p_soc_obj->tx_q_roc, + &curr_tx_ctx->node); + if (ret == QDF_STATUS_SUCCESS) { + p2p_send_tx_conf(curr_tx_ctx, false); + qdf_mem_free(curr_tx_ctx->buf); + qdf_mem_free(curr_tx_ctx); + } else + p2p_err("remove %pK from roc_q fail", + curr_tx_ctx); + } + } + + p2p_debug("clean up tx queue wait for ack, size:%d", + qdf_list_size(&p2p_soc_obj->tx_q_ack)); + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_ack, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + curr_tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_ack, + p_node, &p_node); + if ((vdev_id == P2P_INVALID_VDEV_ID) || + (vdev_id == curr_tx_ctx->vdev_id)) { + ret = qdf_list_remove_node(&p2p_soc_obj->tx_q_ack, + &curr_tx_ctx->node); + if (ret == QDF_STATUS_SUCCESS) { + p2p_disable_tx_timer(curr_tx_ctx); + p2p_send_tx_conf(curr_tx_ctx, false); + qdf_mem_free(curr_tx_ctx->buf); + qdf_mem_free(curr_tx_ctx); + } else + p2p_err("remove %pK from roc_q fail", + curr_tx_ctx); + } + } + + qdf_event_set(&p2p_soc_obj->cleanup_tx_done); + + return QDF_STATUS_SUCCESS; +} + +bool p2p_check_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *random_mac_addr) +{ + uint32_t i = 0; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct wlan_objmgr_vdev *vdev; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_debug("vdev is null"); + return false; + } + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("p2p vdev object is NULL"); + return false; + } + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + if ((p2p_vdev_obj->random_mac[i].in_use) && + (!qdf_mem_cmp(p2p_vdev_obj->random_mac[i].addr, + random_mac_addr, QDF_MAC_ADDR_SIZE))) { + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + return true; + } + } + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return false; +} + +/** + * find_action_frame_cookie() - Checks for action cookie in cookie list + * @cookie_list: List of cookies + * @rnd_cookie: Cookie to be searched + * + * Return: If search is successful return pointer to action_frame_cookie + * object in which cookie item is encapsulated. + */ +static struct action_frame_cookie * +find_action_frame_cookie(qdf_list_t *cookie_list, uint64_t rnd_cookie) +{ + struct action_frame_cookie *action_cookie; + + qdf_list_for_each(cookie_list, action_cookie, cookie_node) { + if (action_cookie->cookie == rnd_cookie) + return action_cookie; + } + + return NULL; +} + +/** + * allocate_action_frame_cookie() - Allocate and add action cookie to + * given list + * @cookie_list: List of cookies + * @rnd_cookie: Cookie to be added + * + * Return: If allocation and addition is successful return pointer to + * action_frame_cookie object in which cookie item is encapsulated. + */ +static struct action_frame_cookie * +allocate_action_frame_cookie(qdf_list_t *cookie_list, uint64_t rnd_cookie) +{ + struct action_frame_cookie *action_cookie; + + action_cookie = qdf_mem_malloc(sizeof(*action_cookie)); + if (!action_cookie) + return NULL; + + action_cookie->cookie = rnd_cookie; + qdf_list_insert_front(cookie_list, &action_cookie->cookie_node); + + return action_cookie; +} + +/** + * delete_action_frame_cookie() - Delete the cookie from given list + * @cookie_list: List of cookies + * @action_cookie: Cookie to be deleted + * + * This function deletes the cookie item from given list and corresponding + * object in which it is encapsulated. + * + * Return: None + */ +static void +delete_action_frame_cookie(qdf_list_t *cookie_list, + struct action_frame_cookie *action_cookie) +{ + qdf_list_remove_node(cookie_list, &action_cookie->cookie_node); + qdf_mem_free(action_cookie); +} + +/** + * append_action_frame_cookie() - Append action cookie to given list + * @cookie_list: List of cookies + * @rnd_cookie: Cookie to be append + * + * This is a wrapper function which invokes allocate_action_frame_cookie + * if the cookie to be added is not duplicate + * + * Return: true - for successful case + * false - failed. + */ +static bool +append_action_frame_cookie(qdf_list_t *cookie_list, uint64_t rnd_cookie) +{ + struct action_frame_cookie *action_cookie; + + /* + * There should be no mac entry with empty cookie list, + * check and ignore if duplicate + */ + action_cookie = find_action_frame_cookie(cookie_list, rnd_cookie); + if (action_cookie) + /* random mac address is already programmed */ + return true; + + /* insert new cookie in cookie list */ + action_cookie = allocate_action_frame_cookie(cookie_list, rnd_cookie); + if (!action_cookie) + return false; + + return true; +} + +/** + * p2p_add_random_mac() - add or append random mac to given vdev rand mac list + * @soc: soc object + * @vdev_id: vdev id + * @mac: mac addr to be added or append + * @freq: frequency + * @rnd_cookie: random mac mgmt tx cookie + * + * This function will add or append the mac addr entry to vdev random mac list. + * Once the mac addr filter is not needed, it can be removed by + * p2p_del_random_mac. + * + * Return: QDF_STATUS_E_EXISTS - append to existing list + * QDF_STATUS_SUCCESS - add a new entry. + * other : failed to add the mac address entry. + */ +static QDF_STATUS +p2p_add_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *mac, uint32_t freq, uint64_t rnd_cookie) +{ + uint32_t i; + uint32_t first_unused = MAX_RANDOM_MAC_ADDRS; + struct action_frame_cookie *action_cookie; + int32_t append_ret; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct wlan_objmgr_vdev *vdev; + + p2p_debug("random_mac:vdev %d mac_addr:%pM rnd_cookie=%llu freq = %u", + vdev_id, mac, rnd_cookie, freq); + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_debug("vdev is null"); + + return QDF_STATUS_E_INVAL; + } + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + p2p_debug("random_mac:p2p vdev object is NULL"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return QDF_STATUS_E_INVAL; + } + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + /* + * Following loop checks whether random mac entry is already + * present, if present get the index of matched entry else + * get the first unused slot to store this new random mac + */ + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + if (!p2p_vdev_obj->random_mac[i].in_use) { + if (first_unused == MAX_RANDOM_MAC_ADDRS) + first_unused = i; + continue; + } + + if (!qdf_mem_cmp(p2p_vdev_obj->random_mac[i].addr, mac, + QDF_MAC_ADDR_SIZE)) + break; + } + + if (i != MAX_RANDOM_MAC_ADDRS) { + append_ret = append_action_frame_cookie( + &p2p_vdev_obj->random_mac[i].cookie_list, + rnd_cookie); + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("random_mac:append %d vdev %d freq %d %pM rnd_cookie %llu", + append_ret, vdev_id, freq, mac, rnd_cookie); + if (!append_ret) { + p2p_debug("random_mac:failed to append rnd_cookie"); + return QDF_STATUS_E_NOMEM; + } + + return QDF_STATUS_E_EXISTS; + } + + if (first_unused == MAX_RANDOM_MAC_ADDRS) { + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("random_mac:Reached the limit of Max random addresses"); + + return QDF_STATUS_E_RESOURCES; + } + + /* get the first unused buf and store new random mac */ + i = first_unused; + + action_cookie = allocate_action_frame_cookie( + &p2p_vdev_obj->random_mac[i].cookie_list, + rnd_cookie); + if (!action_cookie) { + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_err("random_mac:failed to alloc rnd cookie"); + + return QDF_STATUS_E_NOMEM; + } + qdf_mem_copy(p2p_vdev_obj->random_mac[i].addr, mac, QDF_MAC_ADDR_SIZE); + p2p_vdev_obj->random_mac[i].in_use = true; + p2p_vdev_obj->random_mac[i].freq = freq; + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("random_mac:add vdev %d freq %d %pM rnd_cookie %llu", + vdev_id, freq, mac, rnd_cookie); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +p2p_del_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint64_t rnd_cookie, uint32_t duration) +{ + uint32_t i; + struct action_frame_cookie *action_cookie; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct wlan_objmgr_vdev *vdev; + + p2p_debug("random_mac:vdev %d cookie %llu duration %d", vdev_id, + rnd_cookie, duration); + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, vdev_id, + WLAN_P2P_ID); + if (!vdev) { + p2p_debug("vdev is null"); + return QDF_STATUS_E_INVAL; + } + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("p2p vdev object is NULL"); + return QDF_STATUS_E_INVAL; + } + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + struct action_frame_random_mac *random_mac; + + random_mac = &p2p_vdev_obj->random_mac[i]; + if (!random_mac->in_use) + continue; + + action_cookie = find_action_frame_cookie( + &random_mac->cookie_list, rnd_cookie); + if (!action_cookie) + continue; + + delete_action_frame_cookie( + &random_mac->cookie_list, + action_cookie); + + if (qdf_list_empty(&random_mac->cookie_list)) { + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + if (qdf_mc_timer_get_current_state( + &random_mac->clear_timer) == + QDF_TIMER_STATE_RUNNING) + qdf_mc_timer_stop(&random_mac->clear_timer); + qdf_mc_timer_start(&random_mac->clear_timer, duration); + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + p2p_debug("random_mac:noref on vdev %d addr %pM", + vdev_id, random_mac->addr); + } + break; + } + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return QDF_STATUS_SUCCESS; +} + +void p2p_del_all_rand_mac_vdev(struct wlan_objmgr_vdev *vdev) +{ + int32_t i; + uint32_t freq; + uint8_t addr[QDF_MAC_ADDR_SIZE]; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + + if (!vdev) + return; + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) + return; + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + struct action_frame_cookie *action_cookie; + struct action_frame_cookie *action_cookie_next; + + if (!p2p_vdev_obj->random_mac[i].in_use) + continue; + + /* empty the list and clear random addr */ + qdf_list_for_each_del(&p2p_vdev_obj->random_mac[i].cookie_list, + action_cookie, action_cookie_next, + cookie_node) { + qdf_list_remove_node( + &p2p_vdev_obj->random_mac[i].cookie_list, + &action_cookie->cookie_node); + qdf_mem_free(action_cookie); + } + + p2p_vdev_obj->random_mac[i].in_use = false; + freq = p2p_vdev_obj->random_mac[i].freq; + qdf_mem_copy(addr, p2p_vdev_obj->random_mac[i].addr, + QDF_MAC_ADDR_SIZE); + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + qdf_mc_timer_stop(&p2p_vdev_obj->random_mac[i].clear_timer); + p2p_clear_mac_filter(wlan_vdev_get_psoc(vdev), + wlan_vdev_get_id(vdev), addr, freq); + p2p_debug("random_mac:delall vdev %d freq %d addr %pM", + wlan_vdev_get_id(vdev), freq, addr); + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + } + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); +} + +static void +p2p_del_rand_mac_vdev_enum_handler(struct wlan_objmgr_psoc *psoc, + void *obj, void *arg) +{ + struct wlan_objmgr_vdev *vdev = obj; + + if (!vdev) { + p2p_err("random_mac:invalid vdev"); + return; + } + + if (!p2p_is_vdev_support_rand_mac(vdev)) + return; + + p2p_del_all_rand_mac_vdev(vdev); +} + +void p2p_del_all_rand_mac_soc(struct wlan_objmgr_psoc *soc) +{ + if (!soc) { + p2p_err("random_mac:soc object is NULL"); + return; + } + + wlan_objmgr_iterate_obj_list(soc, WLAN_VDEV_OP, + p2p_del_rand_mac_vdev_enum_handler, + NULL, 0, WLAN_P2P_ID); +} + +/** + * p2p_is_random_mac() - check mac addr is random mac for vdev + * @soc: soc object + * @vdev_id: vdev id + * @mac: mac addr to be added or append + * + * This function will check the source mac addr same as vdev's mac addr or not. + * If not same, then the source mac addr should be random mac addr. + * + * Return: true if mac is random mac, otherwise false + */ +static bool +p2p_is_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, uint8_t *mac) +{ + bool ret = false; + struct wlan_objmgr_vdev *vdev; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_debug("random_mac:vdev is null"); + return false; + } + + if (qdf_mem_cmp(wlan_vdev_mlme_get_macaddr(vdev), + mac, QDF_MAC_ADDR_SIZE)) + ret = true; + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return ret; +} + +static void p2p_set_mac_filter_callback(bool result, void *context) +{ + struct osif_request *request; + struct random_mac_priv *priv; + + p2p_debug("random_mac:set random mac filter result %d", result); + request = osif_request_get(context); + if (!request) { + p2p_err("random_mac:invalid response"); + return; + } + + priv = osif_request_priv(request); + priv->result = result; + + osif_request_complete(request); + osif_request_put(request); +} + +QDF_STATUS p2p_process_set_rand_mac_rsp(struct p2p_mac_filter_rsp *resp) +{ + struct wlan_objmgr_psoc *soc; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct wlan_objmgr_vdev *vdev; + + if (!resp || !resp->p2p_soc_obj || !resp->p2p_soc_obj->soc) { + p2p_debug("random_mac:set_filter_req is null"); + return QDF_STATUS_E_INVAL; + } + p2p_debug("random_mac:process rsp on vdev %d status %d", resp->vdev_id, + resp->status); + soc = resp->p2p_soc_obj->soc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, resp->vdev_id, + WLAN_P2P_ID); + if (!vdev) { + p2p_debug("random_mac:vdev is null vdev %d", resp->vdev_id); + return QDF_STATUS_E_INVAL; + } + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("random_mac:p2p_vdev_obj is null vdev %d", + resp->vdev_id); + return QDF_STATUS_E_INVAL; + } + if (!p2p_vdev_obj->pending_req.soc) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("random_mac:no pending set req for vdev %d", + resp->vdev_id); + return QDF_STATUS_E_INVAL; + } + + p2p_debug("random_mac:get pending req on vdev %d set %d mac filter %pM freq %d", + p2p_vdev_obj->pending_req.vdev_id, + p2p_vdev_obj->pending_req.set, p2p_vdev_obj->pending_req.mac, + p2p_vdev_obj->pending_req.freq); + if (p2p_vdev_obj->pending_req.cb) + p2p_vdev_obj->pending_req.cb( + !!resp->status, p2p_vdev_obj->pending_req.req_cookie); + + qdf_mem_zero(&p2p_vdev_obj->pending_req, + sizeof(p2p_vdev_obj->pending_req)); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +p2p_process_set_rand_mac(struct p2p_set_mac_filter_req *set_filter_req) +{ + struct wlan_objmgr_psoc *soc; + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct p2p_set_mac_filter param; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct wlan_objmgr_vdev *vdev; + + if (!set_filter_req || !set_filter_req->soc) { + p2p_debug("random_mac:set_filter_req is null"); + return QDF_STATUS_E_INVAL; + } + p2p_debug("random_mac:vdev %d set %d mac filter %pM freq %d", + set_filter_req->vdev_id, set_filter_req->set, + set_filter_req->mac, set_filter_req->freq); + + soc = set_filter_req->soc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + soc, set_filter_req->vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_debug("random_mac:vdev is null vdev %d", + set_filter_req->vdev_id); + goto get_vdev_failed; + } + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + p2p_debug("random_mac:p2p_vdev_obj is null vdev %d", + set_filter_req->vdev_id); + goto get_p2p_obj_failed; + } + if (p2p_vdev_obj->pending_req.soc) { + p2p_debug("random_mac:Busy on vdev %d set %d mac filter %pM freq %d", + p2p_vdev_obj->pending_req.vdev_id, + p2p_vdev_obj->pending_req.set, + p2p_vdev_obj->pending_req.mac, + p2p_vdev_obj->pending_req.freq); + goto get_p2p_obj_failed; + } + + p2p_ops = p2p_psoc_get_tx_ops(soc); + if (p2p_ops && p2p_ops->set_mac_addr_rx_filter_cmd) { + qdf_mem_zero(¶m, sizeof(param)); + param.vdev_id = set_filter_req->vdev_id; + qdf_mem_copy(param.mac, set_filter_req->mac, + QDF_MAC_ADDR_SIZE); + param.freq = set_filter_req->freq; + param.set = set_filter_req->set; + status = p2p_ops->set_mac_addr_rx_filter_cmd(soc, ¶m); + if (status == QDF_STATUS_SUCCESS && set_filter_req->set) + qdf_mem_copy(&p2p_vdev_obj->pending_req, + set_filter_req, sizeof(*set_filter_req)); + p2p_debug("random_mac:p2p set mac addr rx filter, status:%d", + status); + } + +get_p2p_obj_failed: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + +get_vdev_failed: + if (status != QDF_STATUS_SUCCESS && + set_filter_req->cb) + set_filter_req->cb(false, set_filter_req->req_cookie); + + return status; +} + +/** + * p2p_set_mac_filter() - send set mac addr filter cmd + * @soc: soc + * @vdev_id: vdev id + * @mac: mac addr + * @freq: freq + * @set: set or clear + * @cb: callback func to be called when the request completed. + * @req_cookie: cookie to be returned + * + * This function send set random mac addr filter command to p2p component + * msg core + * + * Return: QDF_STATUS_SUCCESS - if sent successfully. + * otherwise : failed. + */ +static QDF_STATUS +p2p_set_mac_filter(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *mac, uint32_t freq, bool set, + p2p_request_mgr_callback_t cb, void *req_cookie) +{ + struct p2p_set_mac_filter_req *set_filter_req; + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + p2p_debug("random_mac:vdev %d freq %d set %d %pM", + vdev_id, freq, set, mac); + + set_filter_req = qdf_mem_malloc(sizeof(*set_filter_req)); + if (!set_filter_req) + return QDF_STATUS_E_NOMEM; + + set_filter_req->soc = soc; + set_filter_req->vdev_id = vdev_id; + set_filter_req->freq = freq; + qdf_mem_copy(set_filter_req->mac, mac, QDF_MAC_ADDR_SIZE); + set_filter_req->set = set; + set_filter_req->cb = cb; + set_filter_req->req_cookie = req_cookie; + + msg.type = P2P_SET_RANDOM_MAC; + msg.bodyptr = set_filter_req; + msg.callback = p2p_process_cmd; + status = scheduler_post_msg(QDF_MODULE_ID_OS_IF, &msg); + if (status != QDF_STATUS_SUCCESS) + qdf_mem_free(set_filter_req); + + return status; +} + +QDF_STATUS +p2p_clear_mac_filter(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *mac, uint32_t freq) +{ + return p2p_set_mac_filter(soc, vdev_id, mac, freq, false, NULL, NULL); +} + +bool +p2p_is_vdev_support_rand_mac(struct wlan_objmgr_vdev *vdev) +{ + enum QDF_OPMODE mode; + + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode == QDF_STA_MODE || + mode == QDF_P2P_CLIENT_MODE || + mode == QDF_P2P_DEVICE_MODE) + return true; + return false; +} + +/** + * p2p_is_vdev_support_rand_mac_by_id() - check vdev type support random mac + * mgmt tx or not + * @soc: soc obj + * @vdev_id: vdev id + * + * Return: true: support random mac mgmt tx + * false: not support random mac mgmt tx. + */ +static bool +p2p_is_vdev_support_rand_mac_by_id(struct wlan_objmgr_psoc *soc, + uint32_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + bool ret = false; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, vdev_id, WLAN_P2P_ID); + if (!vdev) + return false; + ret = p2p_is_vdev_support_rand_mac(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return ret; +} + +/** + * p2p_set_rand_mac() - set random mac address rx filter + * @soc: soc + * @vdev_id: vdev id + * @mac: mac addr + * @freq: freq + * @rnd_cookie: cookie to be returned + * + * This function will post msg to p2p core to set random mac addr rx filter. + * It will wait the respone and return the result to caller. + * + * Return: true: set successfully + * false: failed + */ +static bool +p2p_set_rand_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *mac, uint32_t freq, uint64_t rnd_cookie) +{ + bool ret = false; + int err; + QDF_STATUS status; + struct osif_request *request; + static const struct osif_request_params params = { + .priv_size = sizeof(struct random_mac_priv), + .timeout_ms = WLAN_WAIT_TIME_SET_RND, + }; + void *req_cookie; + struct random_mac_priv *priv; + + request = osif_request_alloc(¶ms); + if (!request) { + p2p_err("Request allocation failure"); + return false; + } + + req_cookie = osif_request_cookie(request); + + status = p2p_set_mac_filter(soc, vdev_id, mac, freq, true, + p2p_set_mac_filter_callback, req_cookie); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("random_mac:set mac fitler failure %d", status); + } else { + err = osif_request_wait_for_response(request); + if (err) { + p2p_err("random_mac:timeout for set mac fitler %d", + err); + } else { + priv = osif_request_priv(request); + ret = priv->result; + p2p_debug("random_mac:vdev %d freq %d result %d %pM rnd_cookie %llu", + vdev_id, freq, priv->result, mac, rnd_cookie); + } + } + osif_request_put(request); + + return ret; +} + +/** + * p2p_request_random_mac() - request random mac mgmt tx + * @soc: soc + * @vdev_id: vdev id + * @mac: mac addr + * @freq: freq + * @rnd_cookie: cookie to be returned + * @duration: duration of tx timeout + * + * This function will add/append the random mac addr filter entry to vdev. + * If it is new added entry, it will request to set filter in target. + * + * Return: QDF_STATUS_SUCCESS: request successfully + * other: failed + */ +static QDF_STATUS +p2p_request_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *mac, uint32_t freq, uint64_t rnd_cookie, + uint32_t duration) +{ + QDF_STATUS status; + + status = p2p_add_random_mac(soc, vdev_id, mac, freq, rnd_cookie); + if (status == QDF_STATUS_E_EXISTS) + return QDF_STATUS_SUCCESS; + else if (status != QDF_STATUS_SUCCESS) + return status; + + if (!p2p_set_rand_mac(soc, vdev_id, mac, freq, rnd_cookie)) + status = p2p_del_random_mac(soc, vdev_id, rnd_cookie, + duration); + + return status; +} + +void p2p_rand_mac_tx(struct tx_action_context *tx_action) +{ + struct wlan_objmgr_psoc *soc; + QDF_STATUS status; + + if (!tx_action || !tx_action->p2p_soc_obj || + !tx_action->p2p_soc_obj->soc) + return; + soc = tx_action->p2p_soc_obj->soc; + + if (!tx_action->no_ack && tx_action->chan && + tx_action->buf_len > MIN_MAC_HEADER_LEN && + p2p_is_vdev_support_rand_mac_by_id(soc, tx_action->vdev_id) && + p2p_is_random_mac(soc, tx_action->vdev_id, + &tx_action->buf[SRC_MAC_ADDR_OFFSET])) { + status = p2p_request_random_mac( + soc, tx_action->vdev_id, + &tx_action->buf[SRC_MAC_ADDR_OFFSET], + wlan_chan_to_freq(tx_action->chan), + tx_action->id, + tx_action->duration); + if (status == QDF_STATUS_SUCCESS) + tx_action->rand_mac_tx = true; + else + tx_action->rand_mac_tx = false; + } +} + +void +p2p_rand_mac_tx_done(struct wlan_objmgr_psoc *soc, + struct tx_action_context *tx_ctx) +{ + if (!tx_ctx || !tx_ctx->rand_mac_tx || !soc) + return; + + p2p_del_random_mac(soc, tx_ctx->vdev_id, tx_ctx->id, tx_ctx->duration); +} + +/** + * p2p_mac_clear_timeout() - clear random mac filter timeout + * @context: timer context + * + * This function will clear the mac addr rx filter in target if no + * reference to it. + * + * Return: void + */ +static void p2p_mac_clear_timeout(void *context) +{ + struct action_frame_random_mac *random_mac = context; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + uint32_t freq; + uint8_t addr[QDF_MAC_ADDR_SIZE]; + uint32_t vdev_id; + bool clear = false; + + if (!random_mac || !random_mac->p2p_vdev_obj) { + p2p_err("invalid context for mac_clear timeout"); + return; + } + p2p_vdev_obj = random_mac->p2p_vdev_obj; + if (!p2p_vdev_obj || !p2p_vdev_obj->vdev) + return; + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + if (qdf_list_empty(&random_mac->cookie_list)) { + random_mac->in_use = false; + clear = true; + } + freq = random_mac->freq; + qdf_mem_copy(addr, random_mac->addr, QDF_MAC_ADDR_SIZE); + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + + vdev_id = wlan_vdev_get_id(p2p_vdev_obj->vdev); + p2p_debug("random_mac:clear timeout vdev %d %pM freq %d clr %d", + vdev_id, addr, freq, clear); + if (clear) + p2p_clear_mac_filter(wlan_vdev_get_psoc(p2p_vdev_obj->vdev), + vdev_id, addr, freq); +} + +void p2p_init_random_mac_vdev(struct p2p_vdev_priv_obj *p2p_vdev_obj) +{ + int32_t i; + + qdf_spinlock_create(&p2p_vdev_obj->random_mac_lock); + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + qdf_mem_zero(&p2p_vdev_obj->random_mac[i], + sizeof(struct action_frame_random_mac)); + p2p_vdev_obj->random_mac[i].in_use = false; + p2p_vdev_obj->random_mac[i].p2p_vdev_obj = p2p_vdev_obj; + qdf_list_create(&p2p_vdev_obj->random_mac[i].cookie_list, 0); + qdf_mc_timer_init(&p2p_vdev_obj->random_mac[i].clear_timer, + QDF_TIMER_TYPE_SW, p2p_mac_clear_timeout, + &p2p_vdev_obj->random_mac[i]); + } +} + +void p2p_deinit_random_mac_vdev(struct p2p_vdev_priv_obj *p2p_vdev_obj) +{ + int32_t i; + + p2p_del_all_rand_mac_vdev(p2p_vdev_obj->vdev); + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + qdf_mc_timer_destroy(&p2p_vdev_obj->random_mac[i].clear_timer); + qdf_list_destroy(&p2p_vdev_obj->random_mac[i].cookie_list); + } + qdf_spinlock_destroy(&p2p_vdev_obj->random_mac_lock); +} + +QDF_STATUS p2p_process_mgmt_tx(struct tx_action_context *tx_ctx) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *curr_roc_ctx; + uint8_t *mac_to; + QDF_STATUS status; + + status = p2p_tx_context_check_valid(tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("invalid tx action context"); + if (tx_ctx) { + if (tx_ctx->buf) { + p2p_send_tx_conf(tx_ctx, false); + qdf_mem_free(tx_ctx->buf); + } + qdf_mem_free(tx_ctx); + } + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + + p2p_debug("soc:%pK, tx_ctx:%pK, vdev_id:%d, scan_id:%d, roc_cookie:%llx, chan:%d, buf:%pK, len:%d, off_chan:%d, cck:%d, ack:%d, duration:%d", + p2p_soc_obj->soc, tx_ctx, tx_ctx->vdev_id, + tx_ctx->scan_id, tx_ctx->roc_cookie, tx_ctx->chan, + tx_ctx->buf, tx_ctx->buf_len, tx_ctx->off_chan, + tx_ctx->no_cck, tx_ctx->no_ack, tx_ctx->duration); + + status = p2p_get_frame_info(tx_ctx->buf, tx_ctx->buf_len, + &(tx_ctx->frame_info)); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("unsupport frame"); + status = QDF_STATUS_E_INVAL; + goto fail; + } + + /* update P2P connection status with tx frame info */ + mac_to = &(tx_ctx->buf[DST_MAC_ADDR_OFFSET]); + p2p_tx_update_connection_status(p2p_soc_obj, + &(tx_ctx->frame_info), mac_to); + + status = p2p_vdev_check_valid(tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_debug("invalid vdev or vdev mode"); + status = QDF_STATUS_E_INVAL; + goto fail; + } + + /* Do not wait for ack for probe response */ + if (tx_ctx->frame_info.sub_type == P2P_MGMT_PROBE_RSP && + !(tx_ctx->no_ack)) { + p2p_debug("Force set no ack to 1"); + tx_ctx->no_ack = 1; + } + + if (!tx_ctx->off_chan) { + status = p2p_execute_tx_action_frame(tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("execute tx fail"); + goto fail; + } else + return QDF_STATUS_SUCCESS; + } + + /* For off channel tx case */ + curr_roc_ctx = p2p_find_current_roc_ctx(p2p_soc_obj); + if (curr_roc_ctx && (curr_roc_ctx->chan == tx_ctx->chan)) { + if ((curr_roc_ctx->roc_state == ROC_STATE_REQUESTED) || + (curr_roc_ctx->roc_state == ROC_STATE_STARTED)) { + tx_ctx->roc_cookie = (uintptr_t)curr_roc_ctx; + status = qdf_list_insert_back( + &p2p_soc_obj->tx_q_roc, + &tx_ctx->node); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to insert off chan tx context to wait roc req queue"); + goto fail; + } else + return QDF_STATUS_SUCCESS; + } else if (curr_roc_ctx->roc_state == ROC_STATE_ON_CHAN) { + p2p_adjust_tx_wait(tx_ctx); + status = p2p_restart_roc_timer(curr_roc_ctx); + curr_roc_ctx->tx_ctx = tx_ctx; + if (status != QDF_STATUS_SUCCESS) { + p2p_err("restart roc timer fail"); + goto fail; + } + status = p2p_execute_tx_action_frame(tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("execute tx fail"); + goto fail; + } else + return QDF_STATUS_SUCCESS; + } + } + + curr_roc_ctx = p2p_find_roc_by_chan(p2p_soc_obj, tx_ctx->chan); + if (curr_roc_ctx && (curr_roc_ctx->roc_state == ROC_STATE_IDLE)) { + tx_ctx->roc_cookie = (uintptr_t)curr_roc_ctx; + status = qdf_list_insert_back( + &p2p_soc_obj->tx_q_roc, + &tx_ctx->node); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to insert off chan tx context to wait roc req queue"); + goto fail; + } else { + return QDF_STATUS_SUCCESS; + } + } + + status = p2p_roc_req_for_tx_action(tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to request roc before off chan tx"); + goto fail; + } + + return QDF_STATUS_SUCCESS; + +fail: + p2p_send_tx_conf(tx_ctx, false); + qdf_idr_remove(&p2p_soc_obj->p2p_idr, tx_ctx->id); + qdf_mem_free(tx_ctx->buf); + qdf_mem_free(tx_ctx); + + return status; +} + +QDF_STATUS p2p_process_mgmt_tx_cancel( + struct cancel_roc_context *cancel_tx) +{ + bool is_roc_q = false; + bool is_ack_q = false; + struct tx_action_context *cur_tx_ctx; + struct p2p_roc_context *cur_roc_ctx; + struct cancel_roc_context cancel_roc; + + if (!cancel_tx || !(cancel_tx->cookie)) { + p2p_info("invalid cancel info"); + return QDF_STATUS_SUCCESS; + } + + p2p_debug("cookie:0x%llx", cancel_tx->cookie); + + cur_tx_ctx = p2p_find_tx_ctx(cancel_tx->p2p_soc_obj, + cancel_tx->cookie, &is_roc_q, &is_ack_q); + if (cur_tx_ctx) { + if (is_roc_q) { + cancel_roc.p2p_soc_obj = + cancel_tx->p2p_soc_obj; + cancel_roc.cookie = + cur_tx_ctx->roc_cookie; + return p2p_process_cancel_roc_req(&cancel_roc); + } + if (is_ack_q) { + /*Has tx action frame, waiting for ack*/ + p2p_debug("Waiting for ack, cookie %llx", + cancel_tx->cookie); + } + } else { + p2p_debug("Failed to find tx ctx by cookie, cookie %llx", + cancel_tx->cookie); + + cur_roc_ctx = p2p_find_roc_by_tx_ctx(cancel_tx->p2p_soc_obj, + cancel_tx->cookie); + if (cur_roc_ctx) { + p2p_debug("tx ctx:%llx, roc:%pK", + cancel_tx->cookie, cur_roc_ctx); + cancel_roc.p2p_soc_obj = + cancel_tx->p2p_soc_obj; + cancel_roc.cookie = (uintptr_t) cur_roc_ctx; + return p2p_process_cancel_roc_req(&cancel_roc); + } + + p2p_debug("Failed to find roc by tx ctx"); + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_process_mgmt_tx_ack_cnf( + struct p2p_tx_conf_event *tx_cnf_event) +{ + struct p2p_tx_cnf tx_cnf; + struct tx_action_context *tx_ctx; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_start_param *start_param; + + p2p_soc_obj = tx_cnf_event->p2p_soc_obj; + + if (!p2p_soc_obj || !(p2p_soc_obj->start_param)) { + qdf_nbuf_free(tx_cnf_event->nbuf); + p2p_err("Invalid p2p soc object or start parameters"); + return QDF_STATUS_E_INVAL; + } + + tx_ctx = p2p_find_tx_ctx_by_nbuf(p2p_soc_obj, tx_cnf_event->nbuf); + qdf_nbuf_free(tx_cnf_event->nbuf); + if (!tx_ctx) { + p2p_err("can't find tx_ctx, tx ack comes late"); + return QDF_STATUS_SUCCESS; + } + + tx_cnf.vdev_id = tx_ctx->vdev_id; + tx_cnf.action_cookie = (uint64_t)tx_ctx->id; + tx_cnf.buf = tx_ctx->buf; + tx_cnf.buf_len = tx_ctx->buf_len; + tx_cnf.status = tx_cnf_event->status; + + p2p_debug("soc:%pK, vdev_id:%d, action_cookie:%llx, len:%d, status:%d, buf:%pK", + p2p_soc_obj->soc, tx_cnf.vdev_id, + tx_cnf.action_cookie, tx_cnf.buf_len, + tx_cnf.status, tx_cnf.buf); + + p2p_rand_mac_tx_done(p2p_soc_obj->soc, tx_ctx); + + /* disable tx timer */ + p2p_disable_tx_timer(tx_ctx); + + start_param = p2p_soc_obj->start_param; + if (start_param->tx_cnf_cb) + start_param->tx_cnf_cb(start_param->tx_cnf_cb_data, + &tx_cnf); + else + p2p_debug("Got tx conf, but no valid up layer callback"); + + p2p_remove_tx_context(tx_ctx); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_process_rx_mgmt( + struct p2p_rx_mgmt_event *rx_mgmt_event) +{ + struct p2p_rx_mgmt_frame *rx_mgmt; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_start_param *start_param; + struct p2p_frame_info frame_info; + uint8_t *mac_from; + + p2p_soc_obj = rx_mgmt_event->p2p_soc_obj; + rx_mgmt = rx_mgmt_event->rx_mgmt; + + if (!p2p_soc_obj || !(p2p_soc_obj->start_param)) { + p2p_err("Invalid psoc object or start parameters"); + qdf_mem_free(rx_mgmt); + return QDF_STATUS_E_INVAL; + } + + p2p_debug("soc:%pK, frame_len:%d, rx_chan:%d, vdev_id:%d, frm_type:%d, rx_rssi:%d, buf:%pK", + p2p_soc_obj->soc, rx_mgmt->frame_len, + rx_mgmt->rx_chan, rx_mgmt->vdev_id, rx_mgmt->frm_type, + rx_mgmt->rx_rssi, rx_mgmt->buf); + + if (rx_mgmt->frm_type == MGMT_ACTION_VENDOR_SPECIFIC) { + p2p_get_frame_info(rx_mgmt->buf, rx_mgmt->frame_len, + &frame_info); + + /* update P2P connection status with rx frame info */ + mac_from = &(rx_mgmt->buf[SRC_MAC_ADDR_OFFSET]); + p2p_rx_update_connection_status(p2p_soc_obj, + &frame_info, mac_from); + + p2p_debug("action_sub_type %u, action_type %d", + frame_info.public_action_type, + frame_info.action_type); + + if ((frame_info.public_action_type == + P2P_PUBLIC_ACTION_NOT_SUPPORT) && + (frame_info.action_type == + P2P_ACTION_NOT_SUPPORT)) { + p2p_debug("non-p2p frame, drop it"); + qdf_mem_free(rx_mgmt); + return QDF_STATUS_SUCCESS; + } else { + p2p_debug("p2p frame, extend roc accordingly"); + p2p_extend_roc_timer(p2p_soc_obj, &frame_info); + } + } + + if (rx_mgmt->frm_type == MGMT_ACTION_CATEGORY_VENDOR_SPECIFIC) + p2p_get_frame_info(rx_mgmt->buf, rx_mgmt->frame_len, + &frame_info); + + start_param = p2p_soc_obj->start_param; + if (start_param->rx_cb) + start_param->rx_cb(start_param->rx_cb_data, rx_mgmt); + else + p2p_debug("rx mgmt, but no valid up layer callback"); + + qdf_mem_free(rx_mgmt); + + return QDF_STATUS_SUCCESS; +} diff --git a/components/p2p/core/src/wlan_p2p_off_chan_tx.h b/components/p2p/core/src/wlan_p2p_off_chan_tx.h new file mode 100644 index 000000000000..367ecd632587 --- /dev/null +++ b/components/p2p/core/src/wlan_p2p_off_chan_tx.h @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Defines off channel tx API & structures + */ + +#ifndef _WLAN_P2P_OFF_CHAN_TX_H_ +#define _WLAN_P2P_OFF_CHAN_TX_H_ + +#include +#include +#include + +#define P2P_EID_VENDOR 0xdd +#define P2P_ACTION_VENDOR_SPECIFIC_CATEGORY 0x7F +#define P2P_PUBLIC_ACTION_FRAME 0x4 +#define P2P_MAC_MGMT_ACTION 0xD +#define P2P_PUBLIC_ACTION_VENDOR_SPECIFIC 0x9 +#define P2P_NOA_ATTR 0xC + +#define P2P_MAX_NOA_ATTR_LEN 31 +#define P2P_IE_HEADER_LEN 6 +#define P2P_MAX_IE_LENGTH 255 +#define P2P_ACTION_OFFSET 24 +#define P2P_PUBLIC_ACTION_FRAME_TYPE_OFFSET 30 +#define P2P_ACTION_FRAME_TYPE_OFFSET 29 +#define PROBE_RSP_IE_OFFSET 36 + +#define P2P_TX_PKT_MIN_HEADROOM (64) + +#define P2P_OUI "\x50\x6f\x9a\x09" +#define P2P_OUI_SIZE 4 + +#define P2P_ACTION_FRAME_RSP_WAIT 500 +#define P2P_ACTION_FRAME_ACK_WAIT 300 +#define P2P_ACTION_FRAME_TX_TIMEOUT 2000 + +#define DST_MAC_ADDR_OFFSET 4 +#define SRC_MAC_ADDR_OFFSET (DST_MAC_ADDR_OFFSET + QDF_MAC_ADDR_SIZE) + +#define P2P_NOA_STREAM_ARR_SIZE (P2P_MAX_NOA_ATTR_LEN + (2 * P2P_IE_HEADER_LEN)) + +#define P2P_GET_TYPE_FRM_FC(__fc__) (((__fc__) & 0x0F) >> 2) +#define P2P_GET_SUBTYPE_FRM_FC(__fc__) (((__fc__) & 0xF0) >> 4) + +#define WLAN_WAIT_TIME_SET_RND 100 + +struct p2p_soc_priv_obj; +struct cancel_roc_context; +struct p2p_tx_conf_event; +struct p2p_rx_mgmt_event; + +/** + * enum p2p_frame_type - frame type + * @P2P_FRAME_MGMT: mgmt frame + * @P2P_FRAME_NOT_SUPPORT: not support frame type + */ +enum p2p_frame_type { + P2P_FRAME_MGMT = 0, + P2P_FRAME_NOT_SUPPORT, +}; + +/** + * enum p2p_frame_sub_type - frame sub type + * @P2P_MGMT_PROBE_REQ: probe request frame + * @P2P_MGMT_PROBE_RSP: probe response frame + * @P2P_MGMT_ACTION: action frame + * @P2P_MGMT_NOT_SUPPORT: not support sub frame type + */ +enum p2p_frame_sub_type { + P2P_MGMT_PROBE_REQ = 4, + P2P_MGMT_PROBE_RSP, + P2P_MGMT_ACTION = 13, + P2P_MGMT_NOT_SUPPORT, +}; + +/** + * enum p2p_public_action_type - public action frame type + * @P2P_PUBLIC_ACTION_NEG_REQ: go negotiation request frame + * @P2P_PUBLIC_ACTION_NEG_RSP: go negotiation response frame + * @P2P_PUBLIC_ACTION_NEG_CNF: go negotiation confirm frame + * @P2P_PUBLIC_ACTION_INVIT_REQ: p2p invitation request frame + * @P2P_PUBLIC_ACTION_INVIT_RSP: p2p invitation response frame + * @P2P_PUBLIC_ACTION_DEV_DIS_REQ: device discoverability request + * @P2P_PUBLIC_ACTION_DEV_DIS_RSP: device discoverability response + * @P2P_PUBLIC_ACTION_PROV_DIS_REQ: provision discovery request + * @P2P_PUBLIC_ACTION_PROV_DIS_RSP: provision discovery response + * @P2P_PUBLIC_ACTION_GAS_INIT_REQ: gas initial request, + * @P2P_PUBLIC_ACTION_GAS_INIT_RSP: gas initial response + * @P2P_PUBLIC_ACTION_GAS_COMB_REQ: gas comeback request + * @P2P_PUBLIC_ACTION_GAS_COMB_RSP: gas comeback response + * @P2P_PUBLIC_ACTION_NOT_SUPPORT: not support p2p public action frame + */ +enum p2p_public_action_type { + P2P_PUBLIC_ACTION_NEG_REQ = 0, + P2P_PUBLIC_ACTION_NEG_RSP, + P2P_PUBLIC_ACTION_NEG_CNF, + P2P_PUBLIC_ACTION_INVIT_REQ, + P2P_PUBLIC_ACTION_INVIT_RSP, + P2P_PUBLIC_ACTION_DEV_DIS_REQ, + P2P_PUBLIC_ACTION_DEV_DIS_RSP, + P2P_PUBLIC_ACTION_PROV_DIS_REQ, + P2P_PUBLIC_ACTION_PROV_DIS_RSP, + P2P_PUBLIC_ACTION_GAS_INIT_REQ = 10, + P2P_PUBLIC_ACTION_GAS_INIT_RSP, + P2P_PUBLIC_ACTION_GAS_COMB_REQ, + P2P_PUBLIC_ACTION_GAS_COMB_RSP, + P2P_PUBLIC_ACTION_NOT_SUPPORT, +}; + +/** + * enum p2p_action_type - p2p action frame type + * @P2P_ACTION_PRESENCE_REQ: presence request frame + * @P2P_ACTION_PRESENCE_RSP: presence response frame + * @P2P_ACTION_NOT_SUPPORT: not support action frame type + */ +enum p2p_action_type { + P2P_ACTION_PRESENCE_REQ = 1, + P2P_ACTION_PRESENCE_RSP = 2, + P2P_ACTION_NOT_SUPPORT, +}; + +struct p2p_frame_info { + enum p2p_frame_type type; + enum p2p_frame_sub_type sub_type; + enum p2p_public_action_type public_action_type; + enum p2p_action_type action_type; +}; + +/** + * struct tx_action_context - tx action frame context + * @node: Node for next element in the list + * @p2p_soc_obj: Pointer to SoC global p2p private object + * @vdev_id: Vdev id on which this request has come + * @scan_id: Scan id given by scan component for this roc req + * @roc_cookie: Cookie for remain on channel request + * @id: Identifier of this tx context + * @chan: Chan for which this tx has been requested + * @buf: tx buffer + * @buf_len: Length of tx buffer + * @off_chan: Is this off channel tx + * @no_cck: Required cck or not + * @no_ack: Required ack or not + * @duration: Duration for the RoC + * @tx_timer: RoC timer + * @frame_info: Frame type information + */ +struct tx_action_context { + qdf_list_node_t node; + struct p2p_soc_priv_obj *p2p_soc_obj; + int vdev_id; + int scan_id; + uint64_t roc_cookie; + int32_t id; + uint8_t chan; + uint8_t *buf; + int buf_len; + bool off_chan; + bool no_cck; + bool no_ack; + bool rand_mac_tx; + uint32_t duration; + qdf_mc_timer_t tx_timer; + struct p2p_frame_info frame_info; + qdf_nbuf_t nbuf; +}; + +/** + * p2p_rand_mac_tx_done() - process random mac mgmt tx done + * @soc: soc + * @tx_ctx: tx context + * + * This function will remove the random mac addr filter reference. + * + * Return: void + */ +void +p2p_rand_mac_tx_done(struct wlan_objmgr_psoc *soc, + struct tx_action_context *tx_ctx); + +/** + * p2p_clear_mac_filter() - send clear mac addr filter cmd + * @soc: soc + * @vdev_id: vdev id + * @mac: mac addr + * @freq: freq + * + * This function send clear random mac addr filter command to p2p component + * msg core + * + * Return: QDF_STATUS_SUCCESS - if sent successfully. + * otherwise: failed. + */ +QDF_STATUS +p2p_clear_mac_filter(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *mac, uint32_t freq); + +/** + * p2p_is_vdev_support_rand_mac() - check vdev type support random mac mgmt + * tx or not + * @vdev: vdev object + * + * Return: true: support random mac mgmt tx + * false: not support random mac mgmt tx. + */ +bool +p2p_is_vdev_support_rand_mac(struct wlan_objmgr_vdev *vdev); + +/** + * p2p_dump_tx_queue() - dump tx queue + * @p2p_soc_obj: p2p soc private object + * + * This function dumps tx queue and output details about tx context in + * queue. + * + * Return: None + */ +void p2p_dump_tx_queue(struct p2p_soc_priv_obj *p2p_soc_obj); + +/** + * p2p_ready_to_tx_frame() - dump tx queue + * @p2p_soc_obj: p2p soc private object + * @cookie: cookie is pointer to roc + * + * This function find out the tx context in wait for roc queue and tx + * this frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_ready_to_tx_frame(struct p2p_soc_priv_obj *p2p_soc_obj, + uint64_t cookie); + +/** + * p2p_cleanup_tx_sync() - Cleanup tx queue + * @p2p_soc_obj: p2p psoc private object + * @vdev: vdev object + * + * This function cleanup tx context in queue until cancellation done. + * To avoid deadlock, don't call from scheduler thread. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_cleanup_tx_sync( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct wlan_objmgr_vdev *vdev); + +/** + * p2p_process_cleanup_tx_queue() - process the message to cleanup tx + * @param: pointer to cleanup parameters + * + * This function cleanup wait for roc queue and wait for ack queue. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_cleanup_tx_queue( + struct p2p_cleanup_param *param); + +/** + * p2p_process_mgmt_tx() - Process mgmt frame tx request + * @tx_ctx: tx context + * + * This function handles mgmt frame tx request. It will call API from + * mgmt txrx component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_mgmt_tx(struct tx_action_context *tx_ctx); + +/** + * p2p_process_mgmt_tx_cancel() - Process cancel mgmt frame tx request + * @cancel_tx: cancel tx context + * + * This function cancel mgmt frame tx request by cookie. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_mgmt_tx_cancel( + struct cancel_roc_context *cancel_tx); + +/** + * p2p_process_mgmt_tx_ack_cnf() - Process tx ack event + * @tx_cnf_event: tx confirmation event information + * + * This function mgmt frame tx confirmation. It will deliver this + * event to up layer + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_mgmt_tx_ack_cnf( + struct p2p_tx_conf_event *tx_cnf_event); + +/** + * p2p_process_rx_mgmt() - Process rx mgmt frame event + * @rx_mgmt_event: rx mgmt frame event information + * + * This function mgmt frame rx mgmt frame event. It will deliver this + * event to up layer + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_rx_mgmt( + struct p2p_rx_mgmt_event *rx_mgmt_event); + +/** + * p2p_find_tx_ctx_by_nbuf() - find tx context by nbuf + * @p2p_soc_obj: p2p soc object + * @nbuf: pointer to nbuf + * + * This function finds out tx context by nbuf. + * + * Return: pointer to tx context + */ +struct tx_action_context *p2p_find_tx_ctx_by_nbuf( + struct p2p_soc_priv_obj *p2p_soc_obj, void *nbuf); + +#define P2P_80211_FRM_SA_OFFSET 10 + +/** + * p2p_del_random_mac() - del mac fitler from given vdev rand mac list + * @soc: soc object + * @vdev_id: vdev id + * @rnd_cookie: random mac mgmt tx cookie + * @duration: timeout value to flush the addr in target. + * + * This function will del the mac addr filter from vdev random mac addr list. + * If there is no reference to mac addr, it will set a clear timer to flush it + * in target finally. + * + * Return: QDF_STATUS_SUCCESS - del successfully. + * other : failed to del the mac address entry. + */ +QDF_STATUS +p2p_del_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint64_t rnd_cookie, uint32_t duration); + +/** + * p2p_check_random_mac() - check random mac addr or not + * @soc: soc context + * @vdev_id: vdev id + * @random_mac_addr: mac addr to be checked + * + * This function check the input addr is random mac addr or not for vdev. + * + * Return: true if addr is random mac address else false. + */ +bool p2p_check_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *random_mac_addr); + +/** + * p2p_process_set_rand_mac() - process the set random mac command + * @set_filter_req: request data + * + * This function will process the set mac addr filter command. + * + * Return: QDF_STATUS_SUCCESS: if process successfully + * other: failed. + */ +QDF_STATUS p2p_process_set_rand_mac( + struct p2p_set_mac_filter_req *set_filter_req); + +/** + * p2p_process_set_rand_mac_rsp() - process the set random mac response + * @resp: response date + * + * This function will process the set mac addr filter event. + * + * Return: QDF_STATUS_SUCCESS: if process successfully + * other: failed. + */ +QDF_STATUS p2p_process_set_rand_mac_rsp(struct p2p_mac_filter_rsp *resp); + +/** + * p2p_del_all_rand_mac_vdev() - del all random mac filter in vdev + * @vdev: vdev object + * + * This function will del all random mac filter in vdev + * + * Return: void + */ +void p2p_del_all_rand_mac_vdev(struct wlan_objmgr_vdev *vdev); + +/** + * p2p_del_all_rand_mac_soc() - del all random mac filter in soc + * @soc: soc object + * + * This function will del all random mac filter in all vdev of soc + * + * Return: void + */ +void p2p_del_all_rand_mac_soc(struct wlan_objmgr_psoc *soc); + +/** + * p2p_rand_mac_tx() - handle random mac mgmt tx + * @tx_action: tx action context + * + * This function will check whether need to set random mac tx filter for a + * given mgmt tx request and do the mac addr filter process as needed. + * + * Return: void + */ +void p2p_rand_mac_tx(struct tx_action_context *tx_action); + +/** + * p2p_init_random_mac_vdev() - Init random mac data for vdev + * @p2p_vdev_obj: p2p vdev private object + * + * This function will init the per vdev random mac data structure. + * + * Return: void + */ +void p2p_init_random_mac_vdev(struct p2p_vdev_priv_obj *p2p_vdev_obj); + +/** + * p2p_deinit_random_mac_vdev() - Init random mac data for vdev + * @p2p_vdev_obj: p2p vdev private object + * + * This function will deinit the per vdev random mac data structure. + * + * Return: void + */ +void p2p_deinit_random_mac_vdev(struct p2p_vdev_priv_obj *p2p_vdev_obj); + +#endif /* _WLAN_P2P_OFF_CHAN_TX_H_ */ diff --git a/components/p2p/core/src/wlan_p2p_roc.c b/components/p2p/core/src/wlan_p2p_roc.c new file mode 100644 index 000000000000..10db8e74d4c3 --- /dev/null +++ b/components/p2p/core/src/wlan_p2p_roc.c @@ -0,0 +1,929 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains RoC API definitions + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_p2p_public_struct.h" +#include "wlan_p2p_tgt_api.h" +#include "wlan_p2p_ucfg_api.h" +#include "wlan_p2p_roc.h" +#include "wlan_p2p_main.h" +#include "wlan_p2p_off_chan_tx.h" + +/** + * p2p_mgmt_rx_ops() - register or unregister rx callback + * @psoc: psoc object + * @isregister: register if true, unregister if false + * + * This function registers or unregisters rx callback to mgmt txrx + * component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_mgmt_rx_ops(struct wlan_objmgr_psoc *psoc, + bool isregister) +{ + struct mgmt_txrx_mgmt_frame_cb_info frm_cb_info; + QDF_STATUS status; + + p2p_debug("psoc:%pK, is register rx:%d", psoc, isregister); + + frm_cb_info.frm_type = MGMT_PROBE_REQ; + frm_cb_info.mgmt_rx_cb = tgt_p2p_mgmt_frame_rx_cb; + + if (isregister) + status = wlan_mgmt_txrx_register_rx_cb(psoc, + WLAN_UMAC_COMP_P2P, &frm_cb_info, 1); + else + status = wlan_mgmt_txrx_deregister_rx_cb(psoc, + WLAN_UMAC_COMP_P2P, &frm_cb_info, 1); + + return status; +} + +/** + * p2p_scan_start() - Start scan + * @roc_ctx: remain on channel request + * + * This function trigger a start scan request to scan component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_scan_start(struct p2p_roc_context *roc_ctx) +{ + QDF_STATUS status; + struct scan_start_request *req; + struct wlan_objmgr_vdev *vdev; + struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + p2p_soc_obj->soc, roc_ctx->vdev_id, + WLAN_P2P_ID); + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + p2p_err("failed to alloc scan start request"); + status = QDF_STATUS_E_NOMEM; + goto fail; + } + + ucfg_scan_init_default_params(vdev, req); + roc_ctx->scan_id = ucfg_scan_get_scan_id(p2p_soc_obj->soc); + req->vdev = vdev; + req->scan_req.scan_id = roc_ctx->scan_id; + req->scan_req.p2p_scan_type = SCAN_P2P_LISTEN; + req->scan_req.scan_req_id = p2p_soc_obj->scan_req_id; + req->scan_req.chan_list.num_chan = 1; + req->scan_req.chan_list.chan[0].freq = wlan_chan_to_freq(roc_ctx->chan); + req->scan_req.dwell_time_passive = roc_ctx->duration; + req->scan_req.dwell_time_active = 0; + req->scan_req.scan_priority = SCAN_PRIORITY_HIGH; + req->scan_req.num_bssid = 1; + qdf_set_macaddr_broadcast(&req->scan_req.bssid_list[0]); + + status = ucfg_scan_start(req); + + p2p_debug("start scan, scan req id:%d, scan id:%d, status:%d", + p2p_soc_obj->scan_req_id, roc_ctx->scan_id, status); +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return status; +} + +/** + * p2p_scan_abort() - Abort scan + * @roc_ctx: remain on channel request + * + * This function trigger an abort scan request to scan component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_scan_abort(struct p2p_roc_context *roc_ctx) +{ + QDF_STATUS status; + struct scan_cancel_request *req; + struct wlan_objmgr_vdev *vdev; + struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; + + p2p_debug("abort scan, scan req id:%d, scan id:%d", + p2p_soc_obj->scan_req_id, roc_ctx->scan_id); + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + p2p_soc_obj->soc, roc_ctx->vdev_id, + WLAN_P2P_ID); + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + p2p_err("failed to alloc scan cancel request"); + status = QDF_STATUS_E_NOMEM; + goto fail; + } + + req->vdev = vdev; + req->cancel_req.requester = p2p_soc_obj->scan_req_id; + req->cancel_req.scan_id = roc_ctx->scan_id; + req->cancel_req.vdev_id = roc_ctx->vdev_id; + req->cancel_req.req_type = WLAN_SCAN_CANCEL_SINGLE; + + qdf_mtrace(QDF_MODULE_ID_P2P, QDF_MODULE_ID_SCAN, + req->cancel_req.req_type, + req->vdev->vdev_objmgr.vdev_id, req->cancel_req.scan_id); + status = ucfg_scan_cancel(req); + + p2p_debug("abort scan, scan req id:%d, scan id:%d, status:%d", + p2p_soc_obj->scan_req_id, roc_ctx->scan_id, status); +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return status; +} + +/** + * p2p_send_roc_event() - Send roc event + * @roc_ctx: remain on channel request + * @evt: roc event information + * + * This function send out roc event to up layer. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_send_roc_event( + struct p2p_roc_context *roc_ctx, enum p2p_roc_event evt) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_event p2p_evt; + struct p2p_start_param *start_param; + + p2p_soc_obj = roc_ctx->p2p_soc_obj; + if (!p2p_soc_obj || !(p2p_soc_obj->start_param)) { + p2p_err("Invalid p2p soc object or start parameters"); + return QDF_STATUS_E_INVAL; + } + start_param = p2p_soc_obj->start_param; + if (!(start_param->event_cb)) { + p2p_err("Invalid p2p event callback to up layer"); + return QDF_STATUS_E_INVAL; + } + + p2p_evt.vdev_id = roc_ctx->vdev_id; + p2p_evt.roc_event = evt; + p2p_evt.cookie = (uint64_t)roc_ctx->id; + p2p_evt.chan = roc_ctx->chan; + p2p_evt.duration = roc_ctx->duration; + + p2p_debug("p2p soc_obj:%pK, roc_ctx:%pK, vdev_id:%d, roc_event:" + "%d, cookie:%llx, chan:%d, duration:%d", p2p_soc_obj, + roc_ctx, p2p_evt.vdev_id, p2p_evt.roc_event, + p2p_evt.cookie, p2p_evt.chan, p2p_evt.duration); + + start_param->event_cb(start_param->event_cb_data, &p2p_evt); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_destroy_roc_ctx() - destroy roc ctx + * @roc_ctx: remain on channel request + * @up_layer_event: if send uplayer event + * @in_roc_queue: if roc context in roc queue + * + * This function destroy roc context. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_destroy_roc_ctx(struct p2p_roc_context *roc_ctx, + bool up_layer_event, bool in_roc_queue) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; + + p2p_debug("p2p_soc_obj:%pK, roc_ctx:%pK, up_layer_event:%d, in_roc_queue:%d", + p2p_soc_obj, roc_ctx, up_layer_event, in_roc_queue); + + if (up_layer_event) { + if (roc_ctx->roc_state < ROC_STATE_ON_CHAN) + p2p_send_roc_event(roc_ctx, ROC_EVENT_READY_ON_CHAN); + p2p_send_roc_event(roc_ctx, ROC_EVENT_COMPLETED); + } + + if (in_roc_queue) { + status = qdf_list_remove_node(&p2p_soc_obj->roc_q, + (qdf_list_node_t *)roc_ctx); + if (QDF_STATUS_SUCCESS != status) + p2p_err("Failed to remove roc req, status %d", status); + } + + qdf_idr_remove(&p2p_soc_obj->p2p_idr, roc_ctx->id); + qdf_mem_free(roc_ctx); + + return status; +} + +/** + * p2p_execute_cancel_roc_req() - Execute cancel roc request + * @roc_ctx: remain on channel request + * + * This function stop roc timer, abort scan and unregister mgmt rx + * callbak. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_execute_cancel_roc_req( + struct p2p_roc_context *roc_ctx) +{ + QDF_STATUS status; + struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + roc_ctx->roc_state = ROC_STATE_CANCEL_IN_PROG; + qdf_event_reset(&p2p_soc_obj->cancel_roc_done); + status = qdf_mc_timer_stop(&roc_ctx->roc_timer); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to stop roc timer, roc %pK", roc_ctx); + + status = p2p_scan_abort(roc_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to abort scan, status:%d, destroy roc %pK", + status, roc_ctx); + qdf_mc_timer_destroy(&roc_ctx->roc_timer); + p2p_mgmt_rx_ops(p2p_soc_obj->soc, false); + p2p_destroy_roc_ctx(roc_ctx, true, true); + qdf_event_set(&p2p_soc_obj->cancel_roc_done); + return status; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_roc_timeout() - Callback for roc timeout + * @pdata: pointer to p2p soc private object + * + * This function is callback for roc time out. + * + * Return: None + */ +static void p2p_roc_timeout(void *pdata) +{ + struct p2p_roc_context *roc_ctx; + struct p2p_soc_priv_obj *p2p_soc_obj; + + p2p_debug("p2p soc obj:%pK", pdata); + + p2p_soc_obj = pdata; + if (!p2p_soc_obj) { + p2p_err("Invalid p2p soc object"); + return; + } + + roc_ctx = p2p_find_current_roc_ctx(p2p_soc_obj); + if (!roc_ctx) { + p2p_debug("No P2P roc is pending"); + return; + } + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + roc_ctx->p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + if (roc_ctx->roc_state == ROC_STATE_CANCEL_IN_PROG) { + p2p_err("Cancellation already in progress"); + return; + } + p2p_execute_cancel_roc_req(roc_ctx); +} + +/** + * p2p_execute_roc_req() - Execute roc request + * @roc_ctx: remain on channel request + * + * This function init roc timer, start scan and register mgmt rx + * callbak. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_execute_roc_req(struct p2p_roc_context *roc_ctx) +{ + QDF_STATUS status; + uint32_t go_num; + struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + /* prevent runtime suspend */ + qdf_runtime_pm_prevent_suspend(&p2p_soc_obj->roc_runtime_lock); + + status = qdf_mc_timer_init(&roc_ctx->roc_timer, + QDF_TIMER_TYPE_SW, p2p_roc_timeout, + p2p_soc_obj); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to init roc timer, status:%d", status); + goto fail; + } + + roc_ctx->roc_state = ROC_STATE_REQUESTED; + if (roc_ctx->duration < P2P_MAX_ROC_DURATION) { + go_num = policy_mgr_mode_specific_connection_count( + p2p_soc_obj->soc, PM_P2P_GO_MODE, NULL); + p2p_debug("present go number:%d", go_num); + if (go_num) + roc_ctx->duration *= P2P_ROC_DURATION_MULTI_GO_PRESENT; + else + roc_ctx->duration *= P2P_ROC_DURATION_MULTI_GO_ABSENT; + /* this is to protect too huge value if some customers + * give a higher value from supplicant + */ + if (roc_ctx->duration > P2P_MAX_ROC_DURATION) + roc_ctx->duration = P2P_MAX_ROC_DURATION; + } + status = p2p_scan_start(roc_ctx); + if (status != QDF_STATUS_SUCCESS) { + qdf_mc_timer_destroy(&roc_ctx->roc_timer); + p2p_err("Failed to start scan, status:%d", status); + goto fail; + } + +fail: + if (status != QDF_STATUS_SUCCESS) { + p2p_destroy_roc_ctx(roc_ctx, true, true); + qdf_runtime_pm_allow_suspend( + &p2p_soc_obj->roc_runtime_lock); + return status; + } + + p2p_soc_obj->cur_roc_vdev_id = roc_ctx->vdev_id; + status = p2p_mgmt_rx_ops(p2p_soc_obj->soc, true); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to register mgmt rx callback, status:%d", + status); + + return status; +} + +/** + * p2p_find_roc_ctx() - Find out roc context by cookie + * @p2p_soc_obj: p2p psoc private object + * @cookie: cookie is the key to find out roc context + * + * This function find out roc context by cookie from p2p psoc private + * object + * + * Return: Pointer to roc context - success + * NULL - failure + */ +static struct p2p_roc_context *p2p_find_roc_ctx( + struct p2p_soc_priv_obj *p2p_soc_obj, uint64_t cookie) +{ + struct p2p_roc_context *curr_roc_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + p2p_debug("p2p soc obj:%pK, cookie:%llx", p2p_soc_obj, cookie); + + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + curr_roc_ctx = qdf_container_of(p_node, + struct p2p_roc_context, node); + if ((uintptr_t) curr_roc_ctx == cookie) + return curr_roc_ctx; + status = qdf_list_peek_next(&p2p_soc_obj->roc_q, + p_node, &p_node); + } + + return NULL; +} + +/** + * p2p_process_scan_start_evt() - Process scan start event + * @roc_ctx: remain on channel request + * + * This function process scan start event. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_process_scan_start_evt( + struct p2p_roc_context *roc_ctx) +{ + roc_ctx->roc_state = ROC_STATE_STARTED; + p2p_debug("scan started, roc ctx:%pK, scan id:%d", + roc_ctx, roc_ctx->scan_id); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_process_ready_on_channel_evt() - Process ready on channel event + * @roc_ctx: remain on channel request + * + * This function process ready on channel event. Starts roc timer. + * Indicates this event to up layer if this is user request roc. Sends + * mgmt frame if this is off channel rx roc. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_process_ready_on_channel_evt( + struct p2p_roc_context *roc_ctx) +{ + uint64_t cookie; + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status; + + p2p_soc_obj = roc_ctx->p2p_soc_obj; + roc_ctx->roc_state = ROC_STATE_ON_CHAN; + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + status = qdf_mc_timer_start(&roc_ctx->roc_timer, + (roc_ctx->duration + P2P_EVENT_PROPAGATE_TIME)); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Remain on Channel timer start failed"); + if (roc_ctx->roc_type == USER_REQUESTED) { + p2p_debug("user required roc, send roc event"); + status = p2p_send_roc_event(roc_ctx, + ROC_EVENT_READY_ON_CHAN); + } + + cookie = (uintptr_t)roc_ctx; + /* ready to tx frame */ + p2p_ready_to_tx_frame(p2p_soc_obj, cookie); + + return status; +} + +/** + * p2p_process_scan_complete_evt() - Process scan complete event + * @roc_ctx: remain on channel request + * + * This function process scan complete event. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_process_scan_complete_evt( + struct p2p_roc_context *roc_ctx) +{ + QDF_STATUS status; + qdf_list_node_t *next_node; + uint32_t size; + struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + /* allow runtime suspend */ + qdf_runtime_pm_allow_suspend(&p2p_soc_obj->roc_runtime_lock); + + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state(&roc_ctx->roc_timer)) { + status = qdf_mc_timer_stop(&roc_ctx->roc_timer); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to stop roc timer"); + } + + status = qdf_mc_timer_destroy(&roc_ctx->roc_timer); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to destroy roc timer"); + + status = p2p_mgmt_rx_ops(p2p_soc_obj->soc, false); + p2p_soc_obj->cur_roc_vdev_id = P2P_INVALID_VDEV_ID; + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to deregister mgmt rx callback"); + + if (roc_ctx->roc_type == USER_REQUESTED) + status = p2p_send_roc_event(roc_ctx, + ROC_EVENT_COMPLETED); + + p2p_destroy_roc_ctx(roc_ctx, false, true); + qdf_event_set(&p2p_soc_obj->cancel_roc_done); + + size = qdf_list_size(&p2p_soc_obj->roc_q); + p2p_debug("P2P roc queue size is %d", status); + if (size > 0) { + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, + &next_node); + if (QDF_STATUS_SUCCESS != status) { + p2p_err("Failed to peek roc req from front, status %d", + status); + return status; + } + roc_ctx = qdf_container_of(next_node, + struct p2p_roc_context, node); + status = p2p_execute_roc_req(roc_ctx); + } + return status; +} + +QDF_STATUS p2p_mgmt_rx_action_ops(struct wlan_objmgr_psoc *psoc, + bool isregister) +{ + struct mgmt_txrx_mgmt_frame_cb_info frm_cb_info[2]; + QDF_STATUS status; + + p2p_debug("psoc:%pK, is register rx:%d", psoc, isregister); + + frm_cb_info[0].frm_type = MGMT_ACTION_VENDOR_SPECIFIC; + frm_cb_info[0].mgmt_rx_cb = tgt_p2p_mgmt_frame_rx_cb; + frm_cb_info[1].frm_type = MGMT_ACTION_CATEGORY_VENDOR_SPECIFIC; + frm_cb_info[1].mgmt_rx_cb = tgt_p2p_mgmt_frame_rx_cb; + + if (isregister) + status = wlan_mgmt_txrx_register_rx_cb(psoc, + WLAN_UMAC_COMP_P2P, frm_cb_info, 2); + else + status = wlan_mgmt_txrx_deregister_rx_cb(psoc, + WLAN_UMAC_COMP_P2P, frm_cb_info, 2); + + return status; +} + +struct p2p_roc_context *p2p_find_current_roc_ctx( + struct p2p_soc_priv_obj *p2p_soc_obj) +{ + struct p2p_roc_context *roc_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + roc_ctx = qdf_container_of(p_node, + struct p2p_roc_context, node); + if (roc_ctx->roc_state != ROC_STATE_IDLE) { + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id" + ":%d, scan_id:%d, tx ctx:%pK, chan:" + "%d, phy_mode:%d, duration:%d, " + "roc_type:%d, roc_state:%d", + roc_ctx->p2p_soc_obj, roc_ctx, + roc_ctx->vdev_id, roc_ctx->scan_id, + roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + return roc_ctx; + } + status = qdf_list_peek_next(&p2p_soc_obj->roc_q, + p_node, &p_node); + } + + return NULL; +} + +struct p2p_roc_context *p2p_find_roc_by_tx_ctx( + struct p2p_soc_priv_obj *p2p_soc_obj, uint64_t cookie) +{ + struct p2p_roc_context *curr_roc_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + p2p_debug("p2p soc obj:%pK, cookie:%llx", p2p_soc_obj, cookie); + + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + curr_roc_ctx = qdf_container_of(p_node, + struct p2p_roc_context, node); + if ((uintptr_t) curr_roc_ctx->tx_ctx == cookie) + return curr_roc_ctx; + status = qdf_list_peek_next(&p2p_soc_obj->roc_q, + p_node, &p_node); + } + + return NULL; +} + +struct p2p_roc_context *p2p_find_roc_by_chan( + struct p2p_soc_priv_obj *p2p_soc_obj, uint8_t chan) +{ + struct p2p_roc_context *roc_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + roc_ctx = qdf_container_of(p_node, + struct p2p_roc_context, + node); + if (roc_ctx->chan == chan) { + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + roc_ctx->p2p_soc_obj, roc_ctx, + roc_ctx->vdev_id, roc_ctx->scan_id, + roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + return roc_ctx; + } + status = qdf_list_peek_next(&p2p_soc_obj->roc_q, + p_node, &p_node); + } + + return NULL; +} + +QDF_STATUS p2p_restart_roc_timer(struct p2p_roc_context *roc_ctx) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state(&roc_ctx->roc_timer)) { + p2p_debug("roc timer is running"); + status = qdf_mc_timer_stop(&roc_ctx->roc_timer); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to stop roc timer"); + return status; + } + + status = qdf_mc_timer_start(&roc_ctx->roc_timer, + roc_ctx->duration); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Remain on Channel timer start failed"); + } + + return status; +} + +QDF_STATUS p2p_cleanup_roc_sync( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct wlan_objmgr_vdev *vdev) +{ + struct scheduler_msg msg = {0}; + struct p2p_cleanup_param *param; + QDF_STATUS status; + uint32_t vdev_id; + + if (!p2p_soc_obj) { + p2p_err("p2p soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + p2p_debug("p2p_soc_obj:%pK, vdev:%pK", p2p_soc_obj, vdev); + param = qdf_mem_malloc(sizeof(*param)); + if (!param) { + p2p_err("failed to allocate cleanup param"); + return QDF_STATUS_E_NOMEM; + } + + param->p2p_soc_obj = p2p_soc_obj; + if (vdev) + vdev_id = (uint32_t)wlan_vdev_get_id(vdev); + else + vdev_id = P2P_INVALID_VDEV_ID; + param->vdev_id = vdev_id; + qdf_event_reset(&p2p_soc_obj->cleanup_roc_done); + msg.type = P2P_CLEANUP_ROC; + msg.bodyptr = param; + msg.callback = p2p_process_cmd; + status = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, &msg); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to post message"); + qdf_mem_free(param); + return status; + } + + status = qdf_wait_single_event( + &p2p_soc_obj->cleanup_roc_done, + P2P_WAIT_CLEANUP_ROC); + + if (status != QDF_STATUS_SUCCESS) + p2p_err("wait for cleanup roc timeout, %d", status); + + return status; +} + +QDF_STATUS p2p_process_cleanup_roc_queue( + struct p2p_cleanup_param *param) +{ + uint32_t vdev_id; + QDF_STATUS status, ret; + struct p2p_roc_context *roc_ctx; + qdf_list_node_t *p_node; + struct p2p_soc_priv_obj *p2p_soc_obj; + + if (!param || !(param->p2p_soc_obj)) { + p2p_err("Invalid cleanup param"); + return QDF_STATUS_E_FAILURE; + } + + p2p_soc_obj = param->p2p_soc_obj; + vdev_id = param->vdev_id; + + p2p_debug("clean up idle roc request, roc queue size:%d, vdev id:%d", + qdf_list_size(&p2p_soc_obj->roc_q), vdev_id); + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + roc_ctx = qdf_container_of(p_node, + struct p2p_roc_context, node); + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + roc_ctx->p2p_soc_obj, roc_ctx, + roc_ctx->vdev_id, roc_ctx->scan_id, + roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + status = qdf_list_peek_next(&p2p_soc_obj->roc_q, + p_node, &p_node); + if ((roc_ctx->roc_state == ROC_STATE_IDLE) && + ((vdev_id == P2P_INVALID_VDEV_ID) || + (vdev_id == roc_ctx->vdev_id))) { + ret = qdf_list_remove_node( + &p2p_soc_obj->roc_q, + (qdf_list_node_t *)roc_ctx); + if (ret == QDF_STATUS_SUCCESS) + qdf_mem_free(roc_ctx); + else + p2p_err("Failed to remove roc ctx from queue"); + } + } + + p2p_debug("clean up started roc request, roc queue size:%d", + qdf_list_size(&p2p_soc_obj->roc_q)); + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + roc_ctx = qdf_container_of(p_node, + struct p2p_roc_context, node); + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + roc_ctx->p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + status = qdf_list_peek_next(&p2p_soc_obj->roc_q, + p_node, &p_node); + if ((roc_ctx->roc_state != ROC_STATE_IDLE) && + ((vdev_id == P2P_INVALID_VDEV_ID) || + (vdev_id == roc_ctx->vdev_id))) { + if (roc_ctx->roc_state != + ROC_STATE_CANCEL_IN_PROG) + p2p_execute_cancel_roc_req(roc_ctx); + + ret = qdf_wait_single_event( + &p2p_soc_obj->cancel_roc_done, + P2P_WAIT_CANCEL_ROC); + p2p_debug("RoC cancellation done, return:%d", ret); + } + } + + qdf_event_set(&p2p_soc_obj->cleanup_roc_done); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_process_roc_req(struct p2p_roc_context *roc_ctx) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *curr_roc_ctx; + QDF_STATUS status; + uint32_t size; + + p2p_soc_obj = roc_ctx->p2p_soc_obj; + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx_ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + status = qdf_list_insert_back(&p2p_soc_obj->roc_q, + &roc_ctx->node); + if (QDF_STATUS_SUCCESS != status) { + p2p_destroy_roc_ctx(roc_ctx, true, false); + p2p_debug("Failed to insert roc req, status %d", status); + return status; + } + + size = qdf_list_size(&p2p_soc_obj->roc_q); + if (size == 1) { + status = p2p_execute_roc_req(roc_ctx); + } else if (size > 1) { + curr_roc_ctx = p2p_find_current_roc_ctx(p2p_soc_obj); + /*TODO, to handle extend roc */ + } + + return status; +} + +QDF_STATUS p2p_process_cancel_roc_req( + struct cancel_roc_context *cancel_roc_ctx) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *curr_roc_ctx; + QDF_STATUS status; + + p2p_soc_obj = cancel_roc_ctx->p2p_soc_obj; + curr_roc_ctx = p2p_find_roc_ctx(p2p_soc_obj, + cancel_roc_ctx->cookie); + + p2p_debug("p2p soc obj:%pK, cookie:%llx, roc ctx:%pK", + p2p_soc_obj, cancel_roc_ctx->cookie, curr_roc_ctx); + + if (!curr_roc_ctx) { + p2p_debug("Failed to find roc req by cookie, cookie %llx", + cancel_roc_ctx->cookie); + return QDF_STATUS_E_INVAL; + } + + if (curr_roc_ctx->roc_state == ROC_STATE_IDLE) { + status = p2p_destroy_roc_ctx(curr_roc_ctx, true, true); + } else if (curr_roc_ctx->roc_state == + ROC_STATE_CANCEL_IN_PROG) { + p2p_debug("Receive cancel roc req when roc req is canceling, cookie %llx", + cancel_roc_ctx->cookie); + status = QDF_STATUS_SUCCESS; + } else { + status = p2p_execute_cancel_roc_req(curr_roc_ctx); + } + + return status; +} + +void p2p_scan_event_cb(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *curr_roc_ctx; + + p2p_debug("soc:%pK, scan event:%d", arg, event->type); + + p2p_soc_obj = (struct p2p_soc_priv_obj *)arg; + if (!p2p_soc_obj) { + p2p_err("Invalid P2P context"); + return; + } + + curr_roc_ctx = p2p_find_current_roc_ctx(p2p_soc_obj); + if (!curr_roc_ctx) { + p2p_err("Failed to find valid P2P roc context"); + return; + } + + qdf_mtrace(QDF_MODULE_ID_SCAN, QDF_MODULE_ID_P2P, event->type, + event->vdev_id, event->scan_id); + switch (event->type) { + case SCAN_EVENT_TYPE_STARTED: + p2p_process_scan_start_evt(curr_roc_ctx); + break; + case SCAN_EVENT_TYPE_FOREIGN_CHANNEL: + p2p_process_ready_on_channel_evt(curr_roc_ctx); + break; + case SCAN_EVENT_TYPE_COMPLETED: + case SCAN_EVENT_TYPE_DEQUEUED: + case SCAN_EVENT_TYPE_START_FAILED: + p2p_process_scan_complete_evt(curr_roc_ctx); + break; + default: + p2p_debug("drop scan event, %d", event->type); + } +} diff --git a/components/p2p/core/src/wlan_p2p_roc.h b/components/p2p/core/src/wlan_p2p_roc.h new file mode 100644 index 000000000000..5e49b1497647 --- /dev/null +++ b/components/p2p/core/src/wlan_p2p_roc.h @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Defines RoC API & structures + */ + +#ifndef _WLAN_P2P_ROC_H_ +#define _WLAN_P2P_ROC_H_ + +#include +#include +#include + +#define P2P_EVENT_PROPAGATE_TIME 10 +#define P2P_WAIT_CANCEL_ROC 1000 +#define P2P_WAIT_CLEANUP_ROC 2000 +#define P2P_MAX_ROC_DURATION 1500 + +#define P2P_ROC_DURATION_MULTI_GO_PRESENT 6 +#define P2P_ROC_DURATION_MULTI_GO_ABSENT 10 +#define P2P_ACTION_FRAME_DEFAULT_WAIT 200 + +struct wlan_objmgr_vdev; +struct scan_event; + +/** + * enum roc_type - user requested or off channel tx + * @USER_REQUESTED: Requested by supplicant + * @OFF_CHANNEL_TX: Issued internally for off channel tx + */ +enum roc_type { + USER_REQUESTED, + OFF_CHANNEL_TX, +}; + +/** + * enum roc_state - P2P RoC state + * @ROC_STATE_IDLE: RoC not yet started or completed + * @ROC_STATE_REQUESTED: Sent scan command to scan manager + * @ROC_STATE_STARTED: Got started event from scan manager + * @ROC_STATE_ON_CHAN: Got foreign channel event from SCM + * @ROC_STATE_CANCEL_IN_PROG: Requested abort scan to SCM + * @ROC_STATE_INVALID: We should not come to this state + */ +enum roc_state { + ROC_STATE_IDLE = 0, + ROC_STATE_REQUESTED, + ROC_STATE_STARTED, + ROC_STATE_ON_CHAN, + ROC_STATE_CANCEL_IN_PROG, + ROC_STATE_INVALID, +}; + +/** + * struct p2p_roc_context - RoC request context + * @node: Node for next element in the list + * @p2p_soc_obj: Pointer to SoC global p2p private object + * @vdev_id: Vdev id on which this request has come + * @scan_id: Scan id given by scan component for this roc req + * @tx_ctx: TX context if this ROC is for tx MGMT + * @chan: Chan for which this RoC has been requested + * @phy_mode: PHY mode + * @duration: Duration for the RoC + * @roc_type: RoC type User requested or internal + * @roc_timer: RoC timer + * @roc_state: Roc state + * @id: identifier of roc + */ +struct p2p_roc_context { + qdf_list_node_t node; + struct p2p_soc_priv_obj *p2p_soc_obj; + uint32_t vdev_id; + uint32_t scan_id; + void *tx_ctx; + uint8_t chan; + uint8_t phy_mode; + uint32_t duration; + enum roc_type roc_type; + qdf_mc_timer_t roc_timer; + enum roc_state roc_state; + int32_t id; +}; + +/** + * struct cancel_roc_context - p2p cancel roc context + * @p2p_soc_obj: Pointer to SoC global p2p private object + * @cookie: Cookie which is given by supplicant + */ +struct cancel_roc_context { + struct p2p_soc_priv_obj *p2p_soc_obj; + uint64_t cookie; +}; + +/** + * struct p2p_cleanup_param - p2p cleanup parameters + * @p2p_soc_obj: Pointer to SoC global p2p private object + * @vdev_id: vdev id + */ +struct p2p_cleanup_param { + struct p2p_soc_priv_obj *p2p_soc_obj; + uint32_t vdev_id; +}; + +/** + * p2p_mgmt_rx_action_ops() - register or unregister rx action callback + * @psoc: psoc object + * @isregister: register if true, unregister if false + * + * This function registers or unregisters rx action frame callback to + * mgmt txrx component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_mgmt_rx_action_ops(struct wlan_objmgr_psoc *psoc, + bool isregister); + +/** + * p2p_find_current_roc_ctx() - Find out roc context in progressing + * @p2p_soc_obj: p2p psoc private object + * + * This function finds out roc context in progressing from p2p psoc + * private object + * + * Return: Pointer to roc context - success + * NULL - failure + */ +struct p2p_roc_context *p2p_find_current_roc_ctx( + struct p2p_soc_priv_obj *p2p_soc_obj); + +/** + * p2p_find_roc_by_tx_ctx() - Find out roc context by tx context + * @p2p_soc_obj: p2p psoc private object + * @cookie: cookie is the key to find out roc context + * + * This function finds out roc context by tx context from p2p psoc + * private object + * + * Return: Pointer to roc context - success + * NULL - failure + */ +struct p2p_roc_context *p2p_find_roc_by_tx_ctx( + struct p2p_soc_priv_obj *p2p_soc_obj, uint64_t cookie); + +/** + * p2p_find_roc_by_chan() - Find out roc context by channel + * @p2p_soc_obj: p2p psoc private object + * @chan: channel of the ROC + * + * This function finds out roc context by channel from p2p psoc + * private object + * + * Return: Pointer to roc context - success + * NULL - failure + */ +struct p2p_roc_context *p2p_find_roc_by_chan( + struct p2p_soc_priv_obj *p2p_soc_obj, uint8_t chan); + +/** + * p2p_restart_roc_timer() - Restarts roc timer + * @roc_ctx: remain on channel context + * + * This function restarts roc timer with updated duration. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_restart_roc_timer(struct p2p_roc_context *roc_ctx); + +/** + * p2p_cleanup_roc_sync() - Cleanup roc context in queue + * @p2p_soc_obj: p2p psoc private object + * @vdev: vdev object + * + * This function cleanup roc context in queue, include the roc + * context in progressing until cancellation done. To avoid deadlock, + * don't call from scheduler thread. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_cleanup_roc_sync( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct wlan_objmgr_vdev *vdev); + +/** + * p2p_process_cleanup_roc_queue() - process the message to cleanup roc + * @param: pointer to cleanup parameters + * + * This function process the message to cleanup roc context in queue, + * include the roc context in progressing. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_cleanup_roc_queue( + struct p2p_cleanup_param *param); + +/** + * p2p_process_roc_req() - Process roc request + * @roc_ctx: roc request context + * + * This function handles roc request. It will call API from scan/mgmt + * txrx component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_roc_req(struct p2p_roc_context *roc_ctx); + +/** + * p2p_process_cancel_roc_req() - Process cancel roc request + * @cancel_roc_ctx: cancel roc request context + * + * This function cancel roc request by cookie. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_cancel_roc_req( + struct cancel_roc_context *cancel_roc_ctx); + +/** + * p2p_scan_event_cb() - Process scan event + * @vdev: vdev associated to this scan event + * @event: event information + * @arg: registered arguments + * + * This function handles P2P scan event and deliver P2P event to HDD + * layer by registered callback. + * + * Return: None + */ +void p2p_scan_event_cb(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg); + +#endif /* _WLAN_P2P_ROC_H_ */ diff --git a/components/p2p/dispatcher/inc/wlan_p2p_cfg.h b/components/p2p/dispatcher/inc/wlan_p2p_cfg.h new file mode 100644 index 000000000000..3ee037f5a0a0 --- /dev/null +++ b/components/p2p/dispatcher/inc/wlan_p2p_cfg.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(CONFIG_P2P_H__) +#define CONFIG_P2P_H__ + +#include "cfg_define.h" +#include "cfg_converged.h" +#include "qdf_types.h" + +/* + * + * gGoKeepAlivePeriod - P2P GO keep alive period. + * @Min: 1 + * @Max: 65535 + * @Default: 20 + * + * This is P2P GO keep alive period. + * + * Related: None. + * + * Supported Feature: P2P + * + * Usage: Internal/External + * + * + */ +#define CFG_GO_KEEP_ALIVE_PERIOD CFG_INI_UINT( \ + "gGoKeepAlivePeriod", \ + 1, \ + 65535, \ + 20, \ + CFG_VALUE_OR_DEFAULT, \ + "P2P GO keep alive period") + +/* + * + * gGoLinkMonitorPeriod - period where link is idle and where + * we send NULL frame + * @Min: 3 + * @Max: 50 + * @Default: 10 + * + * This is period where link is idle and where we send NULL frame for P2P GO. + * + * Related: None. + * + * Supported Feature: P2P + * + * Usage: Internal/External + * + * + */ +#define CFG_GO_LINK_MONITOR_PERIOD CFG_INI_UINT( \ + "gGoLinkMonitorPeriod", \ + 3, \ + 50, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "period where link is idle and where we send NULL frame") + +/* + * + * isP2pDeviceAddrAdministrated - Enables to derive the P2P MAC address from + * the primary MAC address + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable to derive the P2P MAC address from the + * primary MAC address. + * + * Related: None. + * + * Supported Feature: P2P + * + * Usage: Internal/External + * + * + */ +#define CFG_P2P_DEVICE_ADDRESS_ADMINISTRATED CFG_INI_BOOL( \ + "isP2pDeviceAddrAdministrated", \ + 1, \ + "derive the P2P MAC address from the primary MAC address") + +/* + * + * gSkipDfsChannelInP2pSearch - Skip DFS Channel in case of P2P Search + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to disable(skip) dfs channel in p2p search. + * Related: None. + * + * Supported Feature: DFS P2P + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_SKIP_DFS_IN_P2P_SEARCH CFG_INI_BOOL( \ + "gSkipDfsChannelInP2pSearch", \ + 1, \ + "skip dfs channel in p2p search") + +#define CFG_P2P_ALL \ + CFG(CFG_GO_KEEP_ALIVE_PERIOD) \ + CFG(CFG_GO_LINK_MONITOR_PERIOD) \ + CFG(CFG_P2P_DEVICE_ADDRESS_ADMINISTRATED) \ + CFG(CFG_ENABLE_SKIP_DFS_IN_P2P_SEARCH) + +#endif diff --git a/components/p2p/dispatcher/inc/wlan_p2p_cfg_api.h b/components/p2p/dispatcher/inc/wlan_p2p_cfg_api.h new file mode 100644 index 000000000000..18e86055bcd2 --- /dev/null +++ b/components/p2p/dispatcher/inc/wlan_p2p_cfg_api.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Contains p2p configures interface definitions + */ + +#ifndef _WLAN_P2P_CFG_API_H_ +#define _WLAN_P2P_CFG_API_H_ + +#include + +struct wlan_objmgr_psoc; + +/** + * cfg_p2p_get_go_keepalive_period() - get go keepalive period + * @psoc: pointer to psoc object + * @period: go keepalive period + * + * This function gets go keepalive period to p2p component + */ +QDF_STATUS +cfg_p2p_get_go_keepalive_period(struct wlan_objmgr_psoc *psoc, + uint32_t *period); + +/** + * cfg_p2p_get_go_link_monitor_period() - get go link monitor period + * @psoc: pointer to psoc object + * @period: go link monitor period + * + * This function gets go link monitor period to p2p component + */ +QDF_STATUS +cfg_p2p_get_go_link_monitor_period(struct wlan_objmgr_psoc *psoc, + uint32_t *period); + +/** + * cfg_p2p_get_device_addr_admin() - get enable/disable p2p device + * addr administrated + * @psoc: pointer to psoc object + * @enable: enable/disable p2p device addr administrated + * + * This function gets enable/disable p2p device addr administrated + */ +QDF_STATUS +cfg_p2p_get_device_addr_admin(struct wlan_objmgr_psoc *psoc, + bool *enable); + +/** + * cfg_p2p_get_skip_dfs_channel_p2p_search() - get skip dfs channel + * in p2p search + * @psoc: pointer to psoc object + * @enable: enable/disable skip dfs channel in p2p search + * + * This function gets enable/disable skip dfs channel in p2p search + */ +QDF_STATUS +cfg_p2p_get_skip_dfs_channel_p2p_search(struct wlan_objmgr_psoc *psoc, + bool *enable); + +#endif /* _WLAN_P2P_CFG_API_H_ */ diff --git a/components/p2p/dispatcher/inc/wlan_p2p_public_struct.h b/components/p2p/dispatcher/inc/wlan_p2p_public_struct.h new file mode 100644 index 000000000000..d1c597ff35f1 --- /dev/null +++ b/components/p2p/dispatcher/inc/wlan_p2p_public_struct.h @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Contains p2p public data structure definations + */ + +#ifndef _WLAN_P2P_PUBLIC_STRUCT_H_ +#define _WLAN_P2P_PUBLIC_STRUCT_H_ + +#include + +#define P2P_MAX_NOA_DESC 4 + +/** + * struct p2p_ps_params - P2P powersave related params + * @opp_ps: opportunistic power save + * @ctwindow: CT window + * @count: count + * @duration: duration + * @interval: interval + * @single_noa_duration: single shot noa duration + * @ps_selection: power save selection + * @session_id: session id + */ +struct p2p_ps_params { + uint8_t opp_ps; + uint32_t ctwindow; + uint8_t count; + uint32_t duration; + uint32_t interval; + uint32_t single_noa_duration; + uint8_t ps_selection; + uint8_t session_id; +}; + +/** + * struct p2p_roc_req - P2P roc request + * @vdev_id: Vdev id on which this request has come + * @chan: Chan for which this RoC has been requested + * @phy_mode: PHY mode + * @duration: Duration for the RoC + */ +struct p2p_roc_req { + uint32_t vdev_id; + uint32_t chan; + uint32_t phy_mode; + uint32_t duration; +}; + +/** + * enum p2p_roc_event - P2P RoC event + * @ROC_EVENT_READY_ON_CHAN: RoC has started now + * @ROC_EVENT_COMPLETED: RoC has been completed + * @ROC_EVENT_INAVLID: Invalid event + */ +enum p2p_roc_event { + ROC_EVENT_READY_ON_CHAN = 0, + ROC_EVENT_COMPLETED, + ROC_EVENT_INAVLID, +}; + +/** + * struct p2p_event - p2p event + * @vdev_id: Vdev id + * @roc_event: RoC event + * @cookie: Cookie which is given to supplicant for this roc req + * @chan: Chan for which this RoC has been requested + * @duration: Duration for the RoC + */ +struct p2p_event { + uint32_t vdev_id; + enum p2p_roc_event roc_event; + uint64_t cookie; + uint32_t chan; + uint32_t duration; +}; + +/** + * struct p2p_rx_mgmt_frame - rx mgmt frame structure + * @frame_len: Frame length + * @rx_chan: RX channel + * @vdev_id: Vdev id + * @frm_type: Frame type + * @rx_rssi: RX rssi + * @buf: Buffer address + */ +struct p2p_rx_mgmt_frame { + uint32_t frame_len; + uint32_t rx_chan; + uint32_t vdev_id; + uint32_t frm_type; + uint32_t rx_rssi; + uint8_t buf[1]; +}; + +/** + * struct p2p_tx_cnf - tx confirm structure + * @vdev_id: Vdev id + * @action_cookie: TX cookie for this action frame + * @buf_len: Frame length + * @status: TX status + * @buf: Buffer address + */ +struct p2p_tx_cnf { + uint32_t vdev_id; + uint64_t action_cookie; + uint32_t buf_len; + uint32_t status; + uint8_t *buf; +}; + +/** + * struct p2p_mgmt_tx - p2p mgmt tx structure + * @vdev_id: Vdev id + * @chan: Chan for which this RoC has been requested + * @wait: Duration for the RoC + * @len: Length of tx buffer + * @no_cck: Required cck or not + * @dont_wait_for_ack: Wait for ack or not + * @off_chan: Off channel tx or not + * @buf: TX buffer + */ +struct p2p_mgmt_tx { + uint32_t vdev_id; + uint32_t chan; + uint32_t wait; + uint32_t len; + uint32_t no_cck; + uint32_t dont_wait_for_ack; + uint32_t off_chan; + const uint8_t *buf; +}; + +/** + * struct p2p_set_mac_filter + * @vdev_id: Vdev id + * @mac: mac addr + * @freq: frequency + * @set: set or clear + */ +struct p2p_set_mac_filter { + uint32_t vdev_id; + uint8_t mac[QDF_MAC_ADDR_SIZE]; + uint32_t freq; + bool set; +}; + +/** + * struct p2p_set_mac_filter_evt + * @vdev_id: Vdev id + * @status: target reported result of set mac addr filter + */ +struct p2p_set_mac_filter_evt { + uint32_t vdev_id; + uint32_t status; +}; + +/** + * struct p2p_ps_config + * @vdev_id: Vdev id + * @opp_ps: Opportunistic power save + * @ct_window: CT window + * @count: Count + * @duration: Duration + * @interval: Interval + * @single_noa_duration: Single shot noa duration + * @ps_selection: power save selection + */ +struct p2p_ps_config { + uint32_t vdev_id; + uint32_t opp_ps; + uint32_t ct_window; + uint32_t count; + uint32_t duration; + uint32_t interval; + uint32_t single_noa_duration; + uint32_t ps_selection; +}; + +/** + * struct p2p_lo_start - p2p listen offload start + * @vdev_id: Vdev id + * @ctl_flags: Control flag + * @freq: P2P listen frequency + * @period: Listen offload period + * @interval: Listen offload interval + * @count: Number listen offload intervals + * @dev_types_len: Device types length + * @probe_resp_len: Probe response template length + * @device_types: Device types + * @probe_resp_tmplt: Probe response template + */ +struct p2p_lo_start { + uint32_t vdev_id; + uint32_t ctl_flags; + uint32_t freq; + uint32_t period; + uint32_t interval; + uint32_t count; + uint32_t dev_types_len; + uint32_t probe_resp_len; + uint8_t *device_types; + uint8_t *probe_resp_tmplt; +}; + +/** + * struct p2p_lo_event + * @vdev_id: vdev id + * @reason_code: reason code + */ +struct p2p_lo_event { + uint32_t vdev_id; + uint32_t reason_code; +}; + +/** + * struct noa_descriptor - noa descriptor + * @type_count: 255: continuous schedule, 0: reserved + * @duration: Absent period duration in micro seconds + * @interval: Absent period interval in micro seconds + * @start_time: 32 bit tsf time when in starts + */ +struct noa_descriptor { + uint32_t type_count; + uint32_t duration; + uint32_t interval; + uint32_t start_time; +}; + +/** + * struct p2p_noa_info - p2p noa information + * @index: identifies instance of NOA su element + * @opps_ps: opps ps state of the AP + * @ct_window: ct window in TUs + * @vdev_id: vdev id + * @num_descriptors: number of NOA descriptors + * @noa_desc: noa descriptors + */ +struct p2p_noa_info { + uint32_t index; + uint32_t opps_ps; + uint32_t ct_window; + uint32_t vdev_id; + uint32_t num_desc; + struct noa_descriptor noa_desc[P2P_MAX_NOA_DESC]; +}; + +/** + * struct p2p_protocol_callbacks - callback to non-converged driver + * @is_mgmt_protected: func to get 11w mgmt protection status + */ +struct p2p_protocol_callbacks { + bool (*is_mgmt_protected)(uint32_t vdev_id, const uint8_t *peer_addr); +}; + +#endif /* _WLAN_P2P_PUBLIC_STRUCT_H_ */ diff --git a/components/p2p/dispatcher/inc/wlan_p2p_tgt_api.h b/components/p2p/dispatcher/inc/wlan_p2p_tgt_api.h new file mode 100644 index 000000000000..0ca20749892d --- /dev/null +++ b/components/p2p/dispatcher/inc/wlan_p2p_tgt_api.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Contains p2p south bound interface definitions + */ + +#ifndef _WLAN_P2P_TGT_API_H_ +#define _WLAN_P2P_TGT_API_H_ + +#include +#include + +struct scan_event; +struct wlan_objmgr_psoc; +struct wlan_objmgr_peer; +struct p2p_noa_info; +struct p2p_lo_event; +struct mgmt_rx_event_params; +enum mgmt_frame_type; + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD + +/** + * tgt_p2p_lo_event_cb() - Listen offload stop request + * @psoc: soc object + * @event_info: lo stop event buffer + * + * This function gets called from target interface. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_lo_event_cb(struct wlan_objmgr_psoc *psoc, + struct p2p_lo_event *event_info); + +/** + * tgt_p2p_register_lo_ev_handler() - register lo event + * @psoc: soc object + * + * p2p tgt api to register listen offload event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_register_lo_ev_handler( + struct wlan_objmgr_psoc *psoc); + +/** + * tgt_p2p_unregister_lo_ev_handler() - unregister lo event + * @psoc: soc object + * + * p2p tgt api to unregister listen offload event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_unregister_lo_ev_handler( + struct wlan_objmgr_psoc *psoc); +#else +static inline QDF_STATUS tgt_p2p_register_lo_ev_handler( + struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS tgt_p2p_unregister_lo_ev_handler( + struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * tgt_p2p_register_macaddr_rx_filter_evt_handler() - register add mac rx + * filter status event + * @psoc: soc object + * @register: register or unregister + * + * p2p tgt api to register add mac rx filter status event + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_register_macaddr_rx_filter_evt_handler( + struct wlan_objmgr_psoc *psoc, bool register); + +/** + * tgt_p2p_register_noa_ev_handler() - register noa event + * @psoc: soc object + * + * p2p tgt api to register noa event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_register_noa_ev_handler( + struct wlan_objmgr_psoc *psoc); + +/** + * tgt_p2p_unregister_noa_ev_handler() - unregister noa event + * @psoc: soc object + * + * p2p tgt api to unregister noa event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_unregister_noa_ev_handler( + struct wlan_objmgr_psoc *psoc); + +/** + * tgt_p2p_scan_event_cb() - Callback for scan event + * @vdev: vdev object + * @event: event information + * @arg: registered arguments + * + * This function gets called from scan component when getting P2P + * scan event. + * + * Return: None + */ +void tgt_p2p_scan_event_cb(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg); + +/** + * tgt_p2p_mgmt_download_comp_cb() - Callback for mgmt frame tx + * complete + * @context: tx context + * @buf: buffer address + * @free: need to free or not + * + * This function gets called from mgmt tx/rx component when mgmt + * frame tx complete. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_mgmt_download_comp_cb(void *context, + qdf_nbuf_t buf, bool free); + +/** + * tgt_p2p_mgmt_ota_comp_cb() - Callback for mgmt frame tx ack + * @context: tx context + * @buf: buffer address + * @status: tx status + * @tx_compl_params: tx complete parameters + * + * This function gets called from mgmt tx/rx component when getting + * mgmt frame tx ack. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_mgmt_ota_comp_cb(void *context, qdf_nbuf_t buf, + uint32_t status, void *tx_compl_params); + +/** + * tgt_p2p_mgmt_frame_rx_cb() - Callback for rx mgmt frame + * @psoc: soc context + * @peer: peer context + * @buf: rx buffer + * @mgmt_rx_params: mgmt rx parameters + * @frm_type: frame type + * + * This function gets called from mgmt tx/rx component when rx mgmt + * frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_mgmt_frame_rx_cb(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_peer *peer, qdf_nbuf_t buf, + struct mgmt_rx_event_params *mgmt_rx_params, + enum mgmt_frame_type frm_type); +/** + * tgt_p2p_noa_event_cb() - Callback for noa event + * @psoc: soc object + * @event_info: noa event information + * + * This function gets called from target interface. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_noa_event_cb(struct wlan_objmgr_psoc *psoc, + struct p2p_noa_info *event_info); + +/** + * tgt_p2p_add_mac_addr_status_event_cb() - Callback for set mac addr filter evt + * @psoc: soc object + * @event_info: event information type of p2p_set_mac_filter_evt + * + * This function gets called from target interface. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS +tgt_p2p_add_mac_addr_status_event_cb( + struct wlan_objmgr_psoc *psoc, + struct p2p_set_mac_filter_evt *event_info); + +#endif /* _WLAN_P2P_TGT_API_H_ */ diff --git a/components/p2p/dispatcher/inc/wlan_p2p_ucfg_api.h b/components/p2p/dispatcher/inc/wlan_p2p_ucfg_api.h new file mode 100644 index 000000000000..da397205140f --- /dev/null +++ b/components/p2p/dispatcher/inc/wlan_p2p_ucfg_api.h @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Contains p2p north bound interface definitions + */ + +#ifndef _WLAN_P2P_UCFG_API_H_ +#define _WLAN_P2P_UCFG_API_H_ + +#include + +struct wlan_objmgr_psoc; +struct p2p_roc_req; +struct p2p_event; +struct p2p_rx_mgmt_frame; +struct p2p_tx_cnf; +struct p2p_mgmt_tx; +struct p2p_ps_config; +struct p2p_lo_start; +struct p2p_lo_event; + +/** + * p2p_rx_callback() - Callback for rx mgmt frame + * @user_data: user data associated to this rx mgmt frame. + * @rx_frame: RX mgmt frame + * + * This callback will be used to give rx frames to hdd. + * + * Return: None + */ +typedef void (*p2p_rx_callback)(void *user_data, + struct p2p_rx_mgmt_frame *rx_frame); + +/** + * p2p_action_tx_cnf_callback() - Callback for tx confirmation + * @user_data: user data associated to this tx confirmation + * @tx_cnf: tx confirmation information + * + * This callback will be used to give tx mgmt frame confirmation to + * hdd. + * + * Return: None + */ +typedef void (*p2p_action_tx_cnf_callback)(void *user_data, + struct p2p_tx_cnf *tx_cnf); + +/** + * p2p_lo_event_callback() - Callback for listen offload event + * @user_data: user data associated to this lo event + * @p2p_lo_event: listen offload event information + * + * This callback will be used to give listen offload event to hdd. + * + * Return: None + */ +typedef void (*p2p_lo_event_callback)(void *user_data, + struct p2p_lo_event *p2p_lo_event); + +/** + * p2p_event_callback() - Callback for P2P event + * @user_data: user data associated to this p2p event + * @p2p_event: p2p event information + * + * This callback will be used to give p2p event to hdd. + * + * Return: None + */ +typedef void (*p2p_event_callback)(void *user_data, + struct p2p_event *p2p_event); + +/** + * struct p2p_start_param - p2p soc start parameters. Below callbacks + * will be registered by the HDD + * @rx_callback: Function pointer to hdd rx callback. This + * function will be used to give rx frames to hdd + * @rx_cb_data: RX callback user data + * @event_cb: Founction pointer to hdd p2p event callback. + * This function will be used to give p2p event + * to hdd + * @event_cb_data: Pointer to p2p event callback user data + * @tx_cnf_cb: Function pointer to hdd tx confirm callback. + * This function will be used to give tx confirm + * to hdd + * @tx_cnf_cb_data: Pointer to p2p tx confirm callback user data + * @lo_event_cb: Founction pointer to p2p listen offload + * callback. This function will be used to give + * listen offload stopped event to hdd + * @lo_event_cb_data: Pointer to p2p listen offload callback user data + */ +struct p2p_start_param { + p2p_rx_callback rx_cb; + void *rx_cb_data; + p2p_event_callback event_cb; + void *event_cb_data; + p2p_action_tx_cnf_callback tx_cnf_cb; + void *tx_cnf_cb_data; +#ifdef FEATURE_P2P_LISTEN_OFFLOAD + p2p_lo_event_callback lo_event_cb; + void *lo_event_cb_data; +#endif +}; + +/** + * ucfg_p2p_init() - P2P component initialization + * + * This function gets called when dispatcher initializing. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_init(void); + +/** + * ucfg_p2p_deinit() - P2P component de-init + * + * This function gets called when dispatcher de-init. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_deinit(void); + +/** + * ucfg_p2p_psoc_open() - Open P2P component + * @soc: soc context + * + * This function gets called when dispatcher opening. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_psoc_open(struct wlan_objmgr_psoc *soc); + +/** + * ucfg_p2p_psoc_close() - Close P2P component + * @soc: soc context + * + * This function gets called when dispatcher closing. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_psoc_close(struct wlan_objmgr_psoc *soc); + +/** + * ucfg_p2p_psoc_start() - Start P2P component + * @soc: soc context + * @req: P2P start parameters + * + * This function gets called when up layer starting up. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_psoc_start(struct wlan_objmgr_psoc *soc, + struct p2p_start_param *req); + +/** + * ucfg_p2p_psoc_stop() - Stop P2P component + * @soc: soc context + * + * This function gets called when up layer exit. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_psoc_stop(struct wlan_objmgr_psoc *soc); + +/** + * ucfg_p2p_roc_req() - Roc request + * @soc: soc context + * @roc_req: Roc request parameters + * @cookie: return cookie to caller + * + * This function delivers roc request to P2P component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_roc_req(struct wlan_objmgr_psoc *soc, + struct p2p_roc_req *roc_req, uint64_t *cookie); + +/** + * ucfg_p2p_roc_cancel_req() - Cancel roc request + * @soc: soc context + * @cookie: Find out the roc request by cookie + * + * This function delivers cancel roc request to P2P component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_roc_cancel_req(struct wlan_objmgr_psoc *soc, + uint64_t cookie); + +/** + * ucfg_p2p_cleanup_roc_by_vdev() - Cleanup roc request by vdev + * @vdev: pointer to vdev object + * + * This function call P2P API to cleanup roc request by vdev + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_cleanup_roc_by_vdev(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_cleanup_roc_by_poc() - Cleanup roc request by psoc + * @psoc: pointer to psoc object + * + * This function call P2P API to cleanup roc request by psoc + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_cleanup_roc_by_psoc(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_p2p_cleanup_tx_by_vdev() - Cleanup tx request by vdev + * @vdev: pointer to vdev object + * + * This function call P2P API to cleanup tx action frame request by vdev + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_cleanup_tx_by_vdev(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_cleanup_tx_by_poc() - Cleanup tx request by psoc + * @psoc: pointer to psoc object + * + * This function call P2P API to cleanup tx action frame request by psoc + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_cleanup_tx_by_psoc(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_p2p_mgmt_tx() - Mgmt frame tx request + * @soc: soc context + * @mgmt_frm: TX mgmt frame parameters + * @cookie: Return the cookie to caller + * + * This function delivers mgmt frame tx request to P2P component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_mgmt_tx(struct wlan_objmgr_psoc *soc, + struct p2p_mgmt_tx *mgmt_frm, uint64_t *cookie); + +/** + * ucfg_p2p_mgmt_tx_cancel() - Cancel mgmt frame tx request + * @soc: soc context + * @vdev: vdev object + * @cookie: Find out the mgmt tx request by cookie + * + * This function delivers cancel mgmt frame tx request request to P2P + * component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_mgmt_tx_cancel(struct wlan_objmgr_psoc *soc, + struct wlan_objmgr_vdev *vdev, uint64_t cookie); + +/** + * ucfg_p2p_set_ps() - P2P set power save + * @soc: soc context + * @ps_config: power save configure + * + * This function delivers p2p power save request to P2P component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_set_ps(struct wlan_objmgr_psoc *soc, + struct p2p_ps_config *ps_config); + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +/** + * ucfg_p2p_lo_start() - Listen offload start request + * @soc: soc context + * @p2p_lo_start: lo start parameters + * + * This function delivers listen offload start request to P2P + * component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_lo_start(struct wlan_objmgr_psoc *soc, + struct p2p_lo_start *p2p_lo_start); + +/** + * ucfg_p2p_lo_stop() - Listen offload stop request + * @soc: soc context + * @vdev_id: vdev id + * + * This function delivers listen offload stop request to P2P component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_lo_stop(struct wlan_objmgr_psoc *soc, + uint32_t vdev_id); +#endif + +/** + * p2p_peer_authorized() - Process peer authorized event + * @vdev: vdev structure to which peer is associated + * @mac_addr: peer mac address + * + * This function handles disables noa whenever a legacy station + * complete 4-way handshake after association. + * + * Return: void + */ +void p2p_peer_authorized(struct wlan_objmgr_vdev *vdev, uint8_t *mac_addr); + +/** + * ucfg_p2p_set_noa() - Disable/Enable NOA + * @soc: soc context + * @vdev_id: vdev id + * @disable_noa: TRUE - Disable NoA, FALSE - Enable NoA + * + * This function send wmi command to enable / disable NoA. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_set_noa(struct wlan_objmgr_psoc *soc, + uint32_t vdev_id, bool disable_noa); + +/** + * ucfg_p2p_check_random_mac() - check random mac addr or not + * @soc: soc context + * @vdev_id: vdev id + * @random_mac_addr: mac addr to be checked + * + * This function check the input addr is random mac addr or not for vdev. + * + * Return: true if addr is random mac address else false. + */ +bool ucfg_p2p_check_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *random_mac_addr); + +/** + * ucfg_p2p_register_callbacks() - register p2p callbacks + * @soc: soc context + * @cb_obj: p2p_protocol_callbacks struct + * + * This function registers lim callbacks to p2p components to provide + * protocol information. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_register_callbacks(struct wlan_objmgr_psoc *soc, + struct p2p_protocol_callbacks *cb_obj); + +/** + * ucfg_p2p_status_scan() - Show P2P connection status when scanning + * @vdev: vdev context + * + * This function shows P2P connection status when scanning. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_status_scan(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_status_connect() - Update P2P connection status + * @vdev: vdev context + * + * Updates P2P connection status by up layer when connecting. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_status_connect(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_status_disconnect() - Update P2P connection status + * @vdev: vdev context + * + * Updates P2P connection status by up layer when disconnecting. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_status_disconnect(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_status_start_bss() - Update P2P connection status + * @vdev: vdev context + * + * Updates P2P connection status by up layer when starting bss. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_status_start_bss(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_status_stop_bss() - Update P2P connection status + * @vdev: vdev context + * + * Updates P2P connection status by up layer when stopping bss. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_status_stop_bss(struct wlan_objmgr_vdev *vdev); + +#endif /* _WLAN_P2P_UCFG_API_H_ */ diff --git a/components/p2p/dispatcher/src/wlan_p2p_cfg.c b/components/p2p/dispatcher/src/wlan_p2p_cfg.c new file mode 100644 index 000000000000..490b95821eaf --- /dev/null +++ b/components/p2p/dispatcher/src/wlan_p2p_cfg.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains p2p configures interface definitions + */ + +#include +#include "wlan_p2p_public_struct.h" +#include "wlan_p2p_cfg_api.h" +#include "../../core/src/wlan_p2p_main.h" + +static inline struct p2p_soc_priv_obj * +wlan_psoc_get_p2p_object(struct wlan_objmgr_psoc *psoc) +{ + return wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_P2P); +} + +QDF_STATUS +cfg_p2p_get_go_keepalive_period(struct wlan_objmgr_psoc *psoc, + uint32_t *period) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + + p2p_soc_obj = wlan_psoc_get_p2p_object(psoc); + if (!p2p_soc_obj) { + *period = 0; + p2p_err("p2p psoc null"); + return QDF_STATUS_E_INVAL; + } + + *period = p2p_soc_obj->param.go_keepalive_period; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_p2p_get_go_link_monitor_period(struct wlan_objmgr_psoc *psoc, + uint32_t *period) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + + p2p_soc_obj = wlan_psoc_get_p2p_object(psoc); + if (!p2p_soc_obj) { + *period = 0; + p2p_err("p2p psoc null"); + return QDF_STATUS_E_INVAL; + } + + *period = p2p_soc_obj->param.go_link_monitor_period; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_p2p_get_device_addr_admin(struct wlan_objmgr_psoc *psoc, + bool *enable) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + + p2p_soc_obj = wlan_psoc_get_p2p_object(psoc); + if (!p2p_soc_obj) { + *enable = false; + p2p_err("p2p psoc null"); + return QDF_STATUS_E_INVAL; + } + + *enable = p2p_soc_obj->param.p2p_device_addr_admin; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_p2p_get_skip_dfs_channel_p2p_search(struct wlan_objmgr_psoc *psoc, + bool *enable) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + + p2p_soc_obj = wlan_psoc_get_p2p_object(psoc); + if (!p2p_soc_obj) { + *enable = false; + p2p_err("p2p psoc null"); + return QDF_STATUS_E_INVAL; + } + + *enable = p2p_soc_obj->param.skip_dfs_channel_p2p_search; + + return QDF_STATUS_SUCCESS; +} diff --git a/components/p2p/dispatcher/src/wlan_p2p_tgt_api.c b/components/p2p/dispatcher/src/wlan_p2p_tgt_api.c new file mode 100644 index 000000000000..d6ee448947eb --- /dev/null +++ b/components/p2p/dispatcher/src/wlan_p2p_tgt_api.c @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains p2p south bound interface definitions + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_p2p_tgt_api.h" +#include "wlan_p2p_public_struct.h" +#include "../../core/src/wlan_p2p_main.h" +#include "../../core/src/wlan_p2p_roc.h" +#include "../../core/src/wlan_p2p_off_chan_tx.h" + +#define IEEE80211_FC0_TYPE_MASK 0x0c +#define P2P_NOISE_FLOOR_DBM_DEFAULT (-96) + +static inline struct wlan_lmac_if_p2p_tx_ops * +wlan_psoc_get_p2p_tx_ops(struct wlan_objmgr_psoc *psoc) +{ + return &(psoc->soc_cb.tx_ops.p2p); +} + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +QDF_STATUS tgt_p2p_register_lo_ev_handler( + struct wlan_objmgr_psoc *psoc) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_ops = wlan_psoc_get_p2p_tx_ops(psoc); + if (p2p_ops && p2p_ops->reg_lo_ev_handler) { + status = p2p_ops->reg_lo_ev_handler(psoc, NULL); + p2p_debug("register lo event, status:%d", status); + } + + return status; +} + +QDF_STATUS tgt_p2p_unregister_lo_ev_handler( + struct wlan_objmgr_psoc *psoc) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_ops = wlan_psoc_get_p2p_tx_ops(psoc); + if (p2p_ops && p2p_ops->unreg_lo_ev_handler) { + status = p2p_ops->unreg_lo_ev_handler(psoc, NULL); + p2p_debug("unregister lo event, status:%d", status); + } + + return status; +} + +QDF_STATUS tgt_p2p_lo_event_cb(struct wlan_objmgr_psoc *psoc, + struct p2p_lo_event *event_info) +{ + struct p2p_lo_stop_event *lo_stop_event; + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status; + + p2p_debug("soc:%pK, event_info:%pK", psoc, event_info); + + if (!psoc) { + p2p_err("psoc context passed is NULL"); + if (event_info) + qdf_mem_free(event_info); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc object is NULL"); + if (event_info) + qdf_mem_free(event_info); + return QDF_STATUS_E_INVAL; + } + + if (!event_info) { + p2p_err("invalid lo stop event information"); + return QDF_STATUS_E_INVAL; + } + + lo_stop_event = qdf_mem_malloc(sizeof(*lo_stop_event)); + if (!lo_stop_event) { + p2p_err("Failed to allocate p2p lo stop event"); + qdf_mem_free(event_info); + return QDF_STATUS_E_NOMEM; + } + + lo_stop_event->p2p_soc_obj = p2p_soc_obj; + lo_stop_event->lo_event = event_info; + msg.type = P2P_EVENT_LO_STOPPED; + msg.bodyptr = lo_stop_event; + msg.callback = p2p_process_evt; + msg.flush_callback = p2p_event_flush_callback; + status = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_TARGET_IF, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(lo_stop_event->lo_event); + qdf_mem_free(lo_stop_event); + p2p_err("post msg fail:%d", status); + } + + return status; +} +#endif /* FEATURE_P2P_LISTEN_OFFLOAD */ + +QDF_STATUS +tgt_p2p_add_mac_addr_status_event_cb(struct wlan_objmgr_psoc *psoc, + struct p2p_set_mac_filter_evt *event_info) +{ + struct p2p_mac_filter_rsp *mac_filter_rsp; + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status; + + if (!psoc) { + p2p_err("random_mac:psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + if (!event_info) { + p2p_err("random_mac:invalid event_info"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj( + psoc, WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("random_mac:p2p soc object is NULL"); + return QDF_STATUS_E_INVAL; + } + + mac_filter_rsp = qdf_mem_malloc(sizeof(*mac_filter_rsp)); + if (!mac_filter_rsp) { + p2p_err("random_mac:Failed to allocate mac_filter_rsp"); + return QDF_STATUS_E_NOMEM; + } + + mac_filter_rsp->p2p_soc_obj = p2p_soc_obj; + mac_filter_rsp->vdev_id = event_info->vdev_id; + mac_filter_rsp->status = event_info->status; + + msg.type = P2P_EVENT_ADD_MAC_RSP; + msg.bodyptr = mac_filter_rsp; + msg.callback = p2p_process_evt; + status = scheduler_post_msg(QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) + qdf_mem_free(mac_filter_rsp); + + return status; +} + +QDF_STATUS tgt_p2p_register_macaddr_rx_filter_evt_handler( + struct wlan_objmgr_psoc *psoc, bool reg) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_ops = wlan_psoc_get_p2p_tx_ops(psoc); + if (p2p_ops && p2p_ops->reg_mac_addr_rx_filter_handler) { + status = p2p_ops->reg_mac_addr_rx_filter_handler(psoc, reg); + p2p_debug("register mac addr rx filter event, register %d status:%d", + reg, status); + } + + return status; +} + +QDF_STATUS tgt_p2p_register_noa_ev_handler( + struct wlan_objmgr_psoc *psoc) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_ops = wlan_psoc_get_p2p_tx_ops(psoc); + if (p2p_ops && p2p_ops->reg_noa_ev_handler) { + status = p2p_ops->reg_noa_ev_handler(psoc, NULL); + p2p_debug("register noa event, status:%d", status); + } + + return status; +} + +QDF_STATUS tgt_p2p_unregister_noa_ev_handler( + struct wlan_objmgr_psoc *psoc) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_ops = wlan_psoc_get_p2p_tx_ops(psoc); + if (p2p_ops && p2p_ops->unreg_noa_ev_handler) { + status = p2p_ops->unreg_noa_ev_handler(psoc, NULL); + p2p_debug("unregister noa event, status:%d", status); + } + + return status; +} + +void tgt_p2p_scan_event_cb(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg) +{ + p2p_scan_event_cb(vdev, event, arg); +} + +QDF_STATUS tgt_p2p_mgmt_download_comp_cb(void *context, + qdf_nbuf_t buf, bool free) +{ + p2p_debug("conext:%pK, buf:%pK, free:%d", context, + qdf_nbuf_data(buf), free); + + qdf_nbuf_free(buf); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tgt_p2p_mgmt_ota_comp_cb(void *context, qdf_nbuf_t buf, + uint32_t status, void *tx_compl_params) +{ + struct p2p_tx_conf_event *tx_conf_event; + struct scheduler_msg msg = {0}; + QDF_STATUS ret; + + p2p_debug("context:%pK, buf:%pK, status:%d, tx complete params:%pK", + context, buf, status, tx_compl_params); + + if (!context) { + p2p_err("invalid context"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_INVAL; + } + + tx_conf_event = qdf_mem_malloc(sizeof(*tx_conf_event)); + if (!tx_conf_event) { + p2p_err("Failed to allocate tx cnf event"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_NOMEM; + } + + tx_conf_event->status = status; + tx_conf_event->nbuf = buf; + tx_conf_event->p2p_soc_obj = (struct p2p_soc_priv_obj *)context; + msg.type = P2P_EVENT_MGMT_TX_ACK_CNF; + msg.bodyptr = tx_conf_event; + msg.callback = p2p_process_evt; + msg.flush_callback = p2p_event_flush_callback; + ret = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_TARGET_IF, + &msg); + if (QDF_IS_STATUS_ERROR(ret)) { + qdf_mem_free(tx_conf_event); + qdf_nbuf_free(buf); + p2p_err("post msg fail:%d", status); + } + + return ret; +} + +QDF_STATUS tgt_p2p_mgmt_frame_rx_cb(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_peer *peer, qdf_nbuf_t buf, + struct mgmt_rx_event_params *mgmt_rx_params, + enum mgmt_frame_type frm_type) +{ + struct p2p_rx_mgmt_frame *rx_mgmt; + struct p2p_rx_mgmt_event *rx_mgmt_event; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct scheduler_msg msg = {0}; + struct wlan_objmgr_vdev *vdev; + uint32_t vdev_id; + uint8_t *pdata; + QDF_STATUS status; + + p2p_debug("psoc:%pK, peer:%pK, type:%d", psoc, peer, frm_type); + + if (!mgmt_rx_params) { + p2p_err("mgmt rx params is NULL"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p ctx is NULL, drop this frame"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_FAILURE; + } + + if (!peer) { + if (p2p_soc_obj->cur_roc_vdev_id == P2P_INVALID_VDEV_ID) { + p2p_err("vdev id of current roc invalid"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_FAILURE; + } else { + vdev_id = p2p_soc_obj->cur_roc_vdev_id; + } + } else { + vdev = wlan_peer_get_vdev(peer); + if (!vdev) { + p2p_err("vdev is NULL in peer, drop this frame"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_FAILURE; + } + vdev_id = wlan_vdev_get_id(vdev); + } + + rx_mgmt_event = qdf_mem_malloc(sizeof(*rx_mgmt_event)); + if (!rx_mgmt_event) { + p2p_err("Failed to allocate rx mgmt event"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_NOMEM; + } + + rx_mgmt = qdf_mem_malloc(sizeof(*rx_mgmt) + + mgmt_rx_params->buf_len); + if (!rx_mgmt) { + p2p_err("Failed to allocate rx mgmt frame"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_NOMEM; + } + + pdata = (uint8_t *)qdf_nbuf_data(buf); + rx_mgmt->frame_len = mgmt_rx_params->buf_len; + rx_mgmt->rx_chan = mgmt_rx_params->channel; + rx_mgmt->vdev_id = vdev_id; + rx_mgmt->frm_type = frm_type; + rx_mgmt->rx_rssi = mgmt_rx_params->snr + + P2P_NOISE_FLOOR_DBM_DEFAULT; + rx_mgmt_event->rx_mgmt = rx_mgmt; + rx_mgmt_event->p2p_soc_obj = p2p_soc_obj; + qdf_mem_copy(rx_mgmt->buf, pdata, mgmt_rx_params->buf_len); + msg.type = P2P_EVENT_RX_MGMT; + msg.bodyptr = rx_mgmt_event; + msg.callback = p2p_process_evt; + msg.flush_callback = p2p_event_flush_callback; + status = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_TARGET_IF, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(rx_mgmt_event->rx_mgmt); + qdf_mem_free(rx_mgmt_event); + p2p_err("post msg fail:%d", status); + } + qdf_nbuf_free(buf); + + return status; +} + +QDF_STATUS tgt_p2p_noa_event_cb(struct wlan_objmgr_psoc *psoc, + struct p2p_noa_info *event_info) +{ + struct p2p_noa_event *noa_event; + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status; + + p2p_debug("soc:%pK, event_info:%pK", psoc, event_info); + + if (!psoc) { + p2p_err("psoc context passed is NULL"); + if (event_info) + qdf_mem_free(event_info); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc object is NULL"); + if (event_info) + qdf_mem_free(event_info); + return QDF_STATUS_E_INVAL; + } + + if (!event_info) { + p2p_err("invalid noa event information"); + return QDF_STATUS_E_INVAL; + } + + noa_event = qdf_mem_malloc(sizeof(*noa_event)); + if (!noa_event) { + p2p_err("Failed to allocate p2p noa event"); + qdf_mem_free(event_info); + return QDF_STATUS_E_NOMEM; + } + + noa_event->p2p_soc_obj = p2p_soc_obj; + noa_event->noa_info = event_info; + msg.type = P2P_EVENT_NOA; + msg.bodyptr = noa_event; + msg.callback = p2p_process_evt; + msg.flush_callback = p2p_event_flush_callback; + status = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_TARGET_IF, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(noa_event->noa_info); + qdf_mem_free(noa_event); + p2p_err("post msg fail:%d", status); + } + + return status; +} diff --git a/components/p2p/dispatcher/src/wlan_p2p_ucfg_api.c b/components/p2p/dispatcher/src/wlan_p2p_ucfg_api.c new file mode 100644 index 000000000000..f2a6c38aa6af --- /dev/null +++ b/components/p2p/dispatcher/src/wlan_p2p_ucfg_api.c @@ -0,0 +1,652 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains p2p north bound interface definitions + */ + +#include +#include +#include +#include +#include "wlan_p2p_public_struct.h" +#include "wlan_p2p_ucfg_api.h" +#include "../../core/src/wlan_p2p_main.h" +#include "../../core/src/wlan_p2p_roc.h" +#include "../../core/src/wlan_p2p_off_chan_tx.h" + +static inline struct wlan_lmac_if_p2p_tx_ops * +ucfg_p2p_psoc_get_tx_ops(struct wlan_objmgr_psoc *psoc) +{ + return &(psoc->soc_cb.tx_ops.p2p); +} + +/** + * is_p2p_ps_allowed() - If P2P power save is allowed or not + * @vdev: vdev object + * @id: umac component id + * + * This function returns TRUE if P2P power-save is allowed + * else returns FALSE. + * + * Return: bool + */ +static bool is_p2p_ps_allowed(struct wlan_objmgr_vdev *vdev, + enum wlan_umac_comp_id id) +{ + struct p2p_vdev_priv_obj *p2p_vdev_obj; + uint8_t is_p2pgo = 0; + + if (!vdev) { + p2p_err("vdev:%pK", vdev); + return true; + } + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_P2P); + + if (wlan_vdev_mlme_get_opmode(vdev) == QDF_P2P_GO_MODE) + is_p2pgo = 1; + + if (!p2p_vdev_obj || !is_p2pgo) { + p2p_err("p2p_vdev_obj:%pK is_p2pgo:%u", + p2p_vdev_obj, is_p2pgo); + return false; + } + if (p2p_vdev_obj->non_p2p_peer_count && + p2p_vdev_obj->noa_status == false) { + p2p_debug("non_p2p_peer_count: %u, noa_status: %d", + p2p_vdev_obj->non_p2p_peer_count, + p2p_vdev_obj->noa_status); + return false; + } + + return true; +} + +QDF_STATUS ucfg_p2p_init(void) +{ + return p2p_component_init(); +} + +QDF_STATUS ucfg_p2p_deinit(void) +{ + return p2p_component_deinit(); +} + +QDF_STATUS ucfg_p2p_psoc_open(struct wlan_objmgr_psoc *soc) +{ + return p2p_psoc_object_open(soc); +} + +QDF_STATUS ucfg_p2p_psoc_close(struct wlan_objmgr_psoc *soc) +{ + return p2p_psoc_object_close(soc); +} + +QDF_STATUS ucfg_p2p_psoc_start(struct wlan_objmgr_psoc *soc, + struct p2p_start_param *req) +{ + return p2p_psoc_start(soc, req); +} + +QDF_STATUS ucfg_p2p_psoc_stop(struct wlan_objmgr_psoc *soc) +{ + return p2p_psoc_stop(soc); +} + +QDF_STATUS ucfg_p2p_roc_req(struct wlan_objmgr_psoc *soc, + struct p2p_roc_req *roc_req, uint64_t *cookie) +{ + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *roc_ctx; + QDF_STATUS status; + int32_t id; + + p2p_debug("soc:%pK, vdev_id:%d, chan:%d, phy_mode:%d, duration:%d", + soc, roc_req->vdev_id, roc_req->chan, + roc_req->phy_mode, roc_req->duration); + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("P2P soc object is NULL"); + return QDF_STATUS_E_FAILURE; + } + + roc_ctx = qdf_mem_malloc(sizeof(*roc_ctx)); + if (!roc_ctx) { + p2p_err("failed to allocate p2p roc context"); + return QDF_STATUS_E_NOMEM; + } + + status = qdf_idr_alloc(&p2p_soc_obj->p2p_idr, roc_ctx, &id); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(roc_ctx); + p2p_err("failed to alloc idr, status %d", status); + return status; + } + + *cookie = (uint64_t)id; + roc_ctx->p2p_soc_obj = p2p_soc_obj; + roc_ctx->vdev_id = roc_req->vdev_id; + roc_ctx->chan = roc_req->chan; + roc_ctx->phy_mode = roc_req->phy_mode; + roc_ctx->duration = roc_req->duration; + roc_ctx->roc_state = ROC_STATE_IDLE; + roc_ctx->roc_type = USER_REQUESTED; + roc_ctx->id = id; + msg.type = P2P_ROC_REQ; + msg.bodyptr = roc_ctx; + msg.callback = p2p_process_cmd; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(roc_ctx); + qdf_idr_remove(&p2p_soc_obj->p2p_idr, id); + p2p_err("post msg fail:%d", status); + } + p2p_debug("cookie = 0x%llx", *cookie); + + return status; +} + +QDF_STATUS ucfg_p2p_roc_cancel_req(struct wlan_objmgr_psoc *soc, + uint64_t cookie) +{ + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct cancel_roc_context *cancel_roc; + void *roc_ctx = NULL; + QDF_STATUS status; + + p2p_debug("soc:%pK, cookie:0x%llx", soc, cookie); + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + status = qdf_idr_find(&p2p_soc_obj->p2p_idr, + cookie, &roc_ctx); + if (QDF_IS_STATUS_ERROR(status)) { + p2p_err("invalid id"); + return QDF_STATUS_E_INVAL; + } + + cancel_roc = qdf_mem_malloc(sizeof(*cancel_roc)); + if (!cancel_roc) { + p2p_err("failed to allocate cancel p2p roc"); + return QDF_STATUS_E_NOMEM; + } + + cancel_roc->p2p_soc_obj = p2p_soc_obj; + cancel_roc->cookie = (uintptr_t)roc_ctx; + msg.type = P2P_CANCEL_ROC_REQ; + msg.bodyptr = cancel_roc; + msg.callback = p2p_process_cmd; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, + &msg); + + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(cancel_roc); + p2p_err("post msg fail:%d", status); + } + + return status; +} + +QDF_STATUS ucfg_p2p_cleanup_roc_by_vdev(struct wlan_objmgr_vdev *vdev) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct wlan_objmgr_psoc *psoc; + + p2p_debug("vdev:%pK", vdev); + + if (!vdev) { + p2p_err("null vdev"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + p2p_err("null psoc"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return p2p_cleanup_roc_sync(p2p_soc_obj, vdev); +} + +QDF_STATUS ucfg_p2p_cleanup_roc_by_psoc(struct wlan_objmgr_psoc *psoc) +{ + struct p2p_soc_priv_obj *obj; + + if (!psoc) { + p2p_err("null psoc"); + return QDF_STATUS_E_INVAL; + } + + obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_P2P); + if (!obj) { + p2p_err("null p2p soc obj"); + return QDF_STATUS_E_FAILURE; + } + + return p2p_cleanup_roc_sync(obj, NULL); +} + +QDF_STATUS ucfg_p2p_cleanup_tx_by_vdev(struct wlan_objmgr_vdev *vdev) +{ + struct p2p_soc_priv_obj *obj; + struct wlan_objmgr_psoc *psoc; + + if (!vdev) { + p2p_err("null vdev"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + p2p_err("null psoc"); + return QDF_STATUS_E_INVAL; + } + + obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_P2P); + if (!obj) { + p2p_err("null p2p soc obj"); + return QDF_STATUS_E_FAILURE; + } + p2p_del_all_rand_mac_vdev(vdev); + + return p2p_cleanup_tx_sync(obj, vdev); +} + +QDF_STATUS ucfg_p2p_cleanup_tx_by_psoc(struct wlan_objmgr_psoc *psoc) +{ + struct p2p_soc_priv_obj *obj; + + if (!psoc) { + p2p_err("null psoc"); + return QDF_STATUS_E_INVAL; + } + + obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_P2P); + if (!obj) { + p2p_err("null p2p soc obj"); + return QDF_STATUS_E_FAILURE; + } + p2p_del_all_rand_mac_soc(psoc); + + return p2p_cleanup_tx_sync(obj, NULL); +} + +QDF_STATUS ucfg_p2p_mgmt_tx(struct wlan_objmgr_psoc *soc, + struct p2p_mgmt_tx *mgmt_frm, uint64_t *cookie) +{ + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct tx_action_context *tx_action; + QDF_STATUS status; + int32_t id; + + p2p_debug("soc:%pK, vdev_id:%d, chan:%d, wait:%d, buf_len:%d, cck:%d, no ack:%d, off chan:%d", + soc, mgmt_frm->vdev_id, mgmt_frm->chan, + mgmt_frm->wait, mgmt_frm->len, mgmt_frm->no_cck, + mgmt_frm->dont_wait_for_ack, mgmt_frm->off_chan); + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("P2P soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + tx_action = qdf_mem_malloc(sizeof(*tx_action)); + if (!tx_action) { + p2p_err("Failed to allocate tx action context"); + return QDF_STATUS_E_NOMEM; + } + + /* return cookie just for ota ack frames */ + if (mgmt_frm->dont_wait_for_ack) + id = 0; + else { + status = qdf_idr_alloc(&p2p_soc_obj->p2p_idr, + tx_action, &id); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(tx_action); + p2p_err("failed to alloc idr, status :%d", status); + return status; + } + } + + *cookie = (uint64_t)id; + tx_action->p2p_soc_obj = p2p_soc_obj; + tx_action->vdev_id = mgmt_frm->vdev_id; + tx_action->chan = mgmt_frm->chan; + tx_action->duration = mgmt_frm->wait; + tx_action->buf_len = mgmt_frm->len; + tx_action->no_cck = mgmt_frm->no_cck; + tx_action->no_ack = mgmt_frm->dont_wait_for_ack; + tx_action->off_chan = mgmt_frm->off_chan; + tx_action->buf = qdf_mem_malloc(tx_action->buf_len); + if (!(tx_action->buf)) { + p2p_err("Failed to allocate buffer for action frame"); + qdf_mem_free(tx_action); + return QDF_STATUS_E_NOMEM; + } + qdf_mem_copy(tx_action->buf, mgmt_frm->buf, tx_action->buf_len); + tx_action->nbuf = NULL; + tx_action->id = id; + + p2p_rand_mac_tx(tx_action); + + msg.type = P2P_MGMT_TX; + msg.bodyptr = tx_action; + msg.callback = p2p_process_cmd; + msg.flush_callback = p2p_msg_flush_callback; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + if (id) + qdf_idr_remove(&p2p_soc_obj->p2p_idr, id); + qdf_mem_free(tx_action->buf); + qdf_mem_free(tx_action); + p2p_err("post msg fail:%d", status); + } + + return status; +} + +QDF_STATUS ucfg_p2p_mgmt_tx_cancel(struct wlan_objmgr_psoc *soc, + struct wlan_objmgr_vdev *vdev, uint64_t cookie) +{ + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct cancel_roc_context *cancel_tx; + void *tx_ctx; + QDF_STATUS status; + + p2p_debug("soc:%pK, cookie:0x%llx", soc, cookie); + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + status = qdf_idr_find(&p2p_soc_obj->p2p_idr, + (int32_t)cookie, &tx_ctx); + if (QDF_IS_STATUS_ERROR(status)) { + p2p_debug("invalid id"); + return QDF_STATUS_E_INVAL; + } + p2p_del_random_mac(soc, wlan_vdev_get_id(vdev), cookie, 20); + + cancel_tx = qdf_mem_malloc(sizeof(*cancel_tx)); + if (!cancel_tx) { + p2p_err("Failed to allocate cancel p2p roc"); + return QDF_STATUS_E_NOMEM; + } + + cancel_tx->p2p_soc_obj = p2p_soc_obj; + cancel_tx->cookie = (uintptr_t)tx_ctx; + msg.type = P2P_MGMT_TX_CANCEL; + msg.bodyptr = cancel_tx; + msg.callback = p2p_process_cmd; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(cancel_tx); + p2p_err("post msg fail: %d", status); + } + + return status; +} + +bool ucfg_p2p_check_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *random_mac_addr) +{ + return p2p_check_random_mac(soc, vdev_id, random_mac_addr); +} + +QDF_STATUS ucfg_p2p_set_ps(struct wlan_objmgr_psoc *soc, + struct p2p_ps_config *ps_config) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint16_t obj_id; + struct wlan_objmgr_vdev *vdev; + struct p2p_ps_config go_ps_config; + + p2p_debug("soc:%pK, vdev_id:%d, opp_ps:%d, ct_window:%d, count:%d, duration:%d, duration:%d, ps_selection:%d", + soc, ps_config->vdev_id, ps_config->opp_ps, + ps_config->ct_window, ps_config->count, + ps_config->duration, ps_config->single_noa_duration, + ps_config->ps_selection); + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + for (obj_id = 0; obj_id < WLAN_UMAC_PSOC_MAX_VDEVS; obj_id++) { + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, obj_id, + WLAN_P2P_ID); + if (vdev) { + if (is_p2p_ps_allowed(vdev, WLAN_UMAC_COMP_P2P)) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + break; + } + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("skip p2p set ps vdev %d, NoA is disabled as legacy STA is connected to GO.", + obj_id); + } + } + if (obj_id >= WLAN_UMAC_PSOC_MAX_VDEVS) { + p2p_debug("No GO found!"); + return QDF_STATUS_E_INVAL; + } + go_ps_config = *ps_config; + go_ps_config.vdev_id = obj_id; + + p2p_ops = ucfg_p2p_psoc_get_tx_ops(soc); + if (p2p_ops->set_ps) { + status = p2p_ops->set_ps(soc, &go_ps_config); + p2p_debug("p2p set ps vdev %d, status:%d", obj_id, status); + } + + return status; +} + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +QDF_STATUS ucfg_p2p_lo_start(struct wlan_objmgr_psoc *soc, + struct p2p_lo_start *p2p_lo_start) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_debug("soc:%pK, vdev_id:%d, ctl_flags:%d, freq:%d, period:%d, interval:%d, count:%d, dev_types_len:%d, probe_resp_len:%d, device_types:%pK, probe_resp_tmplt:%pK", + soc, p2p_lo_start->vdev_id, p2p_lo_start->ctl_flags, + p2p_lo_start->freq, p2p_lo_start->period, + p2p_lo_start->interval, p2p_lo_start->count, + p2p_lo_start->dev_types_len, p2p_lo_start->probe_resp_len, + p2p_lo_start->device_types, p2p_lo_start->probe_resp_tmplt); + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_ops = ucfg_p2p_psoc_get_tx_ops(soc); + if (p2p_ops->lo_start) { + status = p2p_ops->lo_start(soc, p2p_lo_start); + p2p_debug("p2p lo start, status:%d", status); + } + + return status; +} + +QDF_STATUS ucfg_p2p_lo_stop(struct wlan_objmgr_psoc *soc, + uint32_t vdev_id) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_debug("soc:%pK, vdev_id:%d", soc, vdev_id); + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_ops = ucfg_p2p_psoc_get_tx_ops(soc); + if (p2p_ops->lo_stop) { + status = p2p_ops->lo_stop(soc, vdev_id); + p2p_debug("p2p lo stop, status:%d", status); + } + + return status; +} +#endif + +QDF_STATUS ucfg_p2p_set_noa(struct wlan_objmgr_psoc *soc, + uint32_t vdev_id, bool disable_noa) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_INVAL; + + p2p_ops = ucfg_p2p_psoc_get_tx_ops(soc); + if (p2p_ops->set_noa) { + status = p2p_ops->set_noa(soc, vdev_id, disable_noa); + p2p_debug("p2p set noa, status:%d", status); + } + + return status; +} + +QDF_STATUS ucfg_p2p_register_callbacks(struct wlan_objmgr_psoc *soc, + struct p2p_protocol_callbacks *cb_obj) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + + if (!soc || !cb_obj) { + p2p_err("psoc %pM cb_obj %pM context passed is NULL", soc, + cb_obj); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc private object is NULL"); + return QDF_STATUS_E_FAILURE; + } + p2p_soc_obj->p2p_cb = *cb_obj; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_p2p_status_scan(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + return p2p_status_scan(vdev); +} + +QDF_STATUS ucfg_p2p_status_connect(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + return p2p_status_connect(vdev); +} + +QDF_STATUS ucfg_p2p_status_disconnect(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + return p2p_status_disconnect(vdev); +} + +QDF_STATUS ucfg_p2p_status_start_bss(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + return p2p_status_start_bss(vdev); +} + +QDF_STATUS ucfg_p2p_status_stop_bss(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + return p2p_status_stop_bss(vdev); +} diff --git a/components/target_if/p2p/inc/target_if_p2p.h b/components/target_if/p2p/inc/target_if_p2p.h new file mode 100644 index 000000000000..1306e781554b --- /dev/null +++ b/components/target_if/p2p/inc/target_if_p2p.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: offload lmac interface APIs for P2P + */ + +#ifndef _TARGET_IF_P2P_H_ +#define _TARGET_IF_P2P_H_ + +#include + +struct wlan_objmgr_psoc; +struct p2p_ps_config; +struct p2p_lo_start; + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD + +/** + * target_if_p2p_register_lo_event_handler() - Register lo event handler + * @psoc: soc object + * @arg: additional argument + * + * Target interface API to register P2P listen offload event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS target_if_p2p_register_lo_event_handler( + struct wlan_objmgr_psoc *psoc, void *arg); + +/** + * target_if_p2p_unregister_lo_event_handler() - Unregister lo event handler + * @psoc: soc object + * @arg: additional argument + * + * Target interface API to unregister P2P listen offload event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS target_if_p2p_unregister_lo_event_handler( + struct wlan_objmgr_psoc *psoc, void *arg); + +/** + * target_if_p2p_lo_start() - Start listen offload + * @psoc: soc object + * @lo_start: lo start information + * + * Target interface API to start listen offload. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS target_if_p2p_lo_start(struct wlan_objmgr_psoc *psoc, + struct p2p_lo_start *lo_start); + +/** + * target_if_p2p_lo_stop() - Stop listen offload + * @psoc: soc object + * @vdev_id: vdev id + * + * Target interface API to stop listen offload. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS target_if_p2p_lo_stop(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id); +#endif + +/** + * target_if_p2p_register_tx_ops() - Register P2P component TX OPS + * @tx_ops: lmac if transmit ops + * + * Return: None + */ +void target_if_p2p_register_tx_ops(struct wlan_lmac_if_tx_ops *tx_ops); + +/** + * target_if_p2p_register_noa_event_handler() - Register noa event handler + * @psoc: soc object + * @arg: additional argument + * + * Target interface API to register P2P noa event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS target_if_p2p_register_noa_event_handler( + struct wlan_objmgr_psoc *psoc, void *arg); + +/** + * target_if_p2p_unregister_noa_event_handler() - Unregister noa event handler + * @psoc: soc object + * @arg: additional argument + * + * Target interface API to unregister P2P listen offload event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS target_if_p2p_unregister_noa_event_handler( + struct wlan_objmgr_psoc *psoc, void *arg); + +/** + * target_if_p2p_set_ps() - Set power save + * @psoc: soc object + * @arg: additional argument + * + * Target interface API to set power save. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS target_if_p2p_set_ps(struct wlan_objmgr_psoc *psoc, + struct p2p_ps_config *ps_config); + +/** + * target_if_p2p_set_noa() - Disable / Enable NOA + * @psoc: soc object + * @vdev_id: vdev id + * @disable_noa: TRUE - Disable NoA, FALSE - Enable NoA + * + * Target interface API to Disable / Enable P2P NOA. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS target_if_p2p_set_noa(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, bool disable_noa); + +#endif /* _TARGET_IF_P2P_H_ */ diff --git a/components/target_if/p2p/src/target_if_p2p.c b/components/target_if/p2p/src/target_if_p2p.c new file mode 100644 index 000000000000..dbce5991f92f --- /dev/null +++ b/components/target_if/p2p/src/target_if_p2p.c @@ -0,0 +1,474 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: offload lmac interface APIs definitions for P2P + */ + +#include +#include +#include "target_if.h" +#include "target_if_p2p.h" +#include "init_deinit_lmac.h" + +static inline struct wlan_lmac_if_p2p_rx_ops * +target_if_psoc_get_p2p_rx_ops(struct wlan_objmgr_psoc *psoc) +{ + return &(psoc->soc_cb.rx_ops.p2p); +} + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +static inline void +target_if_p2p_lo_register_tx_ops(struct wlan_lmac_if_p2p_tx_ops *p2p_tx_ops) +{ + p2p_tx_ops->lo_start = target_if_p2p_lo_start; + p2p_tx_ops->lo_stop = target_if_p2p_lo_stop; + p2p_tx_ops->reg_lo_ev_handler = + target_if_p2p_register_lo_event_handler; + p2p_tx_ops->unreg_lo_ev_handler = + target_if_p2p_unregister_lo_event_handler; +} + +/** + * target_p2p_lo_event_handler() - WMI callback for lo stop event + * @scn: pointer to scn + * @event_buf: event buffer + * @len: buffer length + * + * This function gets called from WMI when triggered wmi event + * wmi_p2p_lo_stop_event_id. + * + * Return: 0 - success + * others - failure + */ +static int target_p2p_lo_event_handler(ol_scn_t scn, uint8_t *data, + uint32_t datalen) +{ + struct wlan_objmgr_psoc *psoc; + struct wmi_unified *wmi_handle; + struct p2p_lo_event *event_info; + struct wlan_lmac_if_p2p_rx_ops *p2p_rx_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + target_if_debug("scn:%pK, data:%pK, datalen:%d", scn, data, datalen); + + if (!scn || !data) { + target_if_err("scn: 0x%pK, data: 0x%pK", scn, data); + return -EINVAL; + } + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("null psoc"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("null wmi handle"); + return -EINVAL; + } + + event_info = qdf_mem_malloc(sizeof(*event_info)); + if (!event_info) { + target_if_err("Failed to allocate p2p lo event"); + return -ENOMEM; + } + + if (wmi_extract_p2p_lo_stop_ev_param(wmi_handle, data, + event_info)) { + target_if_err("Failed to extract wmi p2p lo stop event"); + qdf_mem_free(event_info); + return -EINVAL; + } + + p2p_rx_ops = target_if_psoc_get_p2p_rx_ops(psoc); + if (p2p_rx_ops->lo_ev_handler) { + status = p2p_rx_ops->lo_ev_handler(psoc, event_info); + target_if_debug("call lo event handler, status:%d", + status); + } else { + qdf_mem_free(event_info); + target_if_debug("no valid lo event handler"); + } + + return qdf_status_to_os_return(status); +} + +QDF_STATUS target_if_p2p_register_lo_event_handler( + struct wlan_objmgr_psoc *psoc, void *arg) +{ + int status; + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + target_if_debug("psoc:%pK, arg:%pK", psoc, arg); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_register_event(wmi_handle, + wmi_p2p_lo_stop_event_id, + target_p2p_lo_event_handler); + + target_if_debug("wmi register lo event handle, status:%d", status); + + return status == 0 ? QDF_STATUS_SUCCESS : QDF_STATUS_E_FAILURE; +} + +QDF_STATUS target_if_p2p_unregister_lo_event_handler( + struct wlan_objmgr_psoc *psoc, void *arg) +{ + int status; + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + target_if_debug("psoc:%pK, arg:%pK", psoc, arg); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_unregister_event(wmi_handle, + wmi_p2p_lo_stop_event_id); + + target_if_debug("wmi unregister lo event handle, status:%d", status); + + return status == 0 ? QDF_STATUS_SUCCESS : QDF_STATUS_E_FAILURE; +} + +QDF_STATUS target_if_p2p_lo_start(struct wlan_objmgr_psoc *psoc, + struct p2p_lo_start *lo_start) +{ + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + if (!lo_start) { + target_if_err("lo start parameters is null"); + return QDF_STATUS_E_INVAL; + } + target_if_debug("psoc:%pK, vdev_id:%d", psoc, lo_start->vdev_id); + + return wmi_unified_p2p_lo_start_cmd(wmi_handle, lo_start); +} + +QDF_STATUS target_if_p2p_lo_stop(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id) +{ + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + target_if_debug("psoc:%pK, vdev_id:%d", psoc, vdev_id); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + return wmi_unified_p2p_lo_stop_cmd(wmi_handle, + (uint8_t)vdev_id); +} +#else +static inline void +target_if_p2p_lo_register_tx_ops(struct wlan_lmac_if_p2p_tx_ops *p2p_tx_ops) +{ +} +#endif /* FEATURE_P2P_LISTEN_OFFLOAD */ + +/** + * target_p2p_noa_event_handler() - WMI callback for noa event + * @scn: pointer to scn + * @event_buf: event buffer + * @len: buffer length + * + * This function gets called from WMI when triggered WMI event + * wmi_p2p_noa_event_id. + * + * Return: 0 - success + * others - failure + */ +static int target_p2p_noa_event_handler(ol_scn_t scn, uint8_t *data, + uint32_t datalen) +{ + struct wlan_objmgr_psoc *psoc; + struct wmi_unified *wmi_handle; + struct p2p_noa_info *event_info; + struct wlan_lmac_if_p2p_rx_ops *p2p_rx_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + target_if_debug("scn:%pK, data:%pK, datalen:%d", scn, data, datalen); + + if (!scn || !data) { + target_if_err("scn: 0x%pK, data: 0x%pK", scn, data); + return -EINVAL; + } + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("null psoc"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("null wmi handle"); + return -EINVAL; + } + + event_info = qdf_mem_malloc(sizeof(*event_info)); + if (!event_info) { + target_if_err("failed to allocate p2p noa information"); + return -ENOMEM; + } + + if (wmi_extract_p2p_noa_ev_param(wmi_handle, data, + event_info)) { + target_if_err("failed to extract wmi p2p noa event"); + qdf_mem_free(event_info); + return -EINVAL; + } + + p2p_rx_ops = target_if_psoc_get_p2p_rx_ops(psoc); + if (p2p_rx_ops->noa_ev_handler) { + status = p2p_rx_ops->noa_ev_handler(psoc, event_info); + target_if_debug("call noa event handler, status:%d", + status); + } else { + qdf_mem_free(event_info); + target_if_debug("no valid noa event handler"); + } + + return qdf_status_to_os_return(status); +} + +QDF_STATUS target_if_p2p_register_noa_event_handler( + struct wlan_objmgr_psoc *psoc, void *arg) +{ + int status; + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + target_if_debug("psoc:%pK, arg:%pK", psoc, arg); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_register_event(wmi_handle, + wmi_p2p_noa_event_id, + target_p2p_noa_event_handler); + + target_if_debug("wmi register noa event handle, status:%d", + status); + + return status == 0 ? QDF_STATUS_SUCCESS : QDF_STATUS_E_FAILURE; +} + +QDF_STATUS target_if_p2p_unregister_noa_event_handler( + struct wlan_objmgr_psoc *psoc, void *arg) +{ + int status; + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + target_if_debug("psoc:%pK, arg:%pK", psoc, arg); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + status = wmi_unified_unregister_event(wmi_handle, + wmi_p2p_noa_event_id); + + target_if_debug("wmi unregister noa event handle, status:%d", + status); + + return status == 0 ? QDF_STATUS_SUCCESS : QDF_STATUS_E_FAILURE; +} + +QDF_STATUS target_if_p2p_set_ps(struct wlan_objmgr_psoc *psoc, + struct p2p_ps_config *ps_config) +{ + struct p2p_ps_params cmd; + QDF_STATUS status; + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + if (!ps_config) { + target_if_err("ps config parameters is null"); + return QDF_STATUS_E_INVAL; + } + + target_if_debug("psoc:%pK, vdev_id:%d, opp_ps:%d", psoc, + ps_config->vdev_id, ps_config->opp_ps); + + cmd.opp_ps = ps_config->opp_ps; + cmd.ctwindow = ps_config->ct_window; + cmd.count = ps_config->count; + cmd.duration = ps_config->duration; + cmd.interval = ps_config->interval; + cmd.single_noa_duration = ps_config->single_noa_duration; + cmd.ps_selection = ps_config->ps_selection; + cmd.session_id = ps_config->vdev_id; + + if (ps_config->opp_ps) + status = wmi_unified_set_p2pgo_oppps_req(wmi_handle, + &cmd); + else + status = wmi_unified_set_p2pgo_noa_req_cmd(wmi_handle, + &cmd); + + if (status != QDF_STATUS_SUCCESS) + target_if_err("Failed to send set uapsd param, %d", + status); + + return status; +} + +QDF_STATUS target_if_p2p_set_noa(struct wlan_objmgr_psoc *psoc, + uint32_t vdev_id, bool disable_noa) +{ + struct vdev_set_params param; + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + target_if_debug("psoc:%pK, vdev_id:%d disable_noa:%d", + psoc, vdev_id, disable_noa); + param.if_id = vdev_id; + param.param_id = WMI_VDEV_PARAM_DISABLE_NOA_P2P_GO; + param.param_value = (uint32_t)disable_noa; + + return wmi_unified_vdev_set_param_send(wmi_handle, ¶m); +} + +static int target_p2p_mac_rx_filter_event_handler(ol_scn_t scn, uint8_t *data, + uint32_t datalen) +{ + struct wlan_objmgr_psoc *psoc; + struct wmi_unified *wmi_handle; + struct p2p_set_mac_filter_evt event_info; + struct wlan_lmac_if_p2p_rx_ops *p2p_rx_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (!scn || !data) { + target_if_err("scn: 0x%pK, data: 0x%pK", scn, data); + return -EINVAL; + } + + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("null psoc"); + return -EINVAL; + } + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("null wmi handle"); + return -EINVAL; + } + + if (wmi_extract_mac_addr_rx_filter_evt_param(wmi_handle, data, + &event_info)) { + target_if_err("failed to extract wmi p2p noa event"); + return -EINVAL; + } + target_if_debug("vdev_id %d status %d", event_info.vdev_id, + event_info.status); + p2p_rx_ops = target_if_psoc_get_p2p_rx_ops(psoc); + if (p2p_rx_ops && p2p_rx_ops->add_mac_addr_filter_evt_handler) + status = p2p_rx_ops->add_mac_addr_filter_evt_handler( + psoc, &event_info); + else + target_if_debug("no add mac addr filter event handler"); + + return qdf_status_to_os_return(status); +} + +static QDF_STATUS target_if_p2p_register_macaddr_rx_filter_evt_handler( + struct wlan_objmgr_psoc *psoc, bool reg) +{ + int status; + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + target_if_debug("psoc:%pK, register %d mac addr rx evt", psoc, reg); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + if (reg) + status = wmi_unified_register_event( + wmi_handle, + wmi_vdev_add_macaddr_rx_filter_event_id, + target_p2p_mac_rx_filter_event_handler); + else + status = wmi_unified_unregister_event( + wmi_handle, + wmi_vdev_add_macaddr_rx_filter_event_id); + + return status == 0 ? QDF_STATUS_SUCCESS : QDF_STATUS_E_FAILURE; +} + +static QDF_STATUS target_if_p2p_set_mac_addr_rx_filter_cmd( + struct wlan_objmgr_psoc *psoc, struct p2p_set_mac_filter *param) +{ + wmi_unified_t wmi_handle = lmac_get_wmi_unified_hdl(psoc); + + if (!wmi_handle) { + target_if_err("Invalid wmi handle"); + return QDF_STATUS_E_INVAL; + } + + return wmi_send_set_mac_addr_rx_filter_cmd(wmi_handle, param); +} + +void target_if_p2p_register_tx_ops(struct wlan_lmac_if_tx_ops *tx_ops) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_tx_ops; + + if (!tx_ops) { + target_if_err("lmac tx_ops is null"); + return; + } + + p2p_tx_ops = &tx_ops->p2p; + p2p_tx_ops->set_ps = target_if_p2p_set_ps; + p2p_tx_ops->set_noa = target_if_p2p_set_noa; + p2p_tx_ops->reg_noa_ev_handler = + target_if_p2p_register_noa_event_handler; + p2p_tx_ops->unreg_noa_ev_handler = + target_if_p2p_unregister_noa_event_handler; + p2p_tx_ops->reg_mac_addr_rx_filter_handler = + target_if_p2p_register_macaddr_rx_filter_evt_handler; + p2p_tx_ops->set_mac_addr_rx_filter_cmd = + target_if_p2p_set_mac_addr_rx_filter_cmd; + /* register P2P listen offload callbacks */ + target_if_p2p_lo_register_tx_ops(p2p_tx_ops); +} diff --git a/components/target_if/tdls/inc/target_if_tdls.h b/components/target_if/tdls/inc/target_if_tdls.h new file mode 100644 index 000000000000..7fe21cdf0c4e --- /dev/null +++ b/components/target_if/tdls/inc/target_if_tdls.h @@ -0,0 +1,116 @@ +/* + * 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: offload lmac interface APIs for tdls + * + */ + +#ifndef __TARGET_IF_TDLS_H__ +#define __TARGET_IF_TDLS_H__ + +struct tdls_info; +struct wlan_objmgr_psoc; +struct tdls_peer_update_state; +struct tdls_channel_switch_params; +struct sta_uapsd_trig_params; + +/** + * target_if_tdls_update_fw_state() - lmac handler to update tdls fw state + * @psoc: psoc object + * @param: tdls state parameter + * + * Return: QDF_STATUS + */ +QDF_STATUS +target_if_tdls_update_fw_state(struct wlan_objmgr_psoc *psoc, + struct tdls_info *param); + +/** + * target_if_tdls_update_peer_state() - lmac handler to update tdls peer state + * @psoc: psoc object + * @peer_params: tdls peer state params + * + * Return: QDF_STATUS + */ +QDF_STATUS +target_if_tdls_update_peer_state(struct wlan_objmgr_psoc *psoc, + struct tdls_peer_update_state *peer_params); + +/** + * target_if_tdls_set_offchan_mode() - lmac handler to set tdls off channel mode + * @psoc: psoc object + * @params: tdls channel swithc params + * + * Return: QDF_STATUS + */ +QDF_STATUS +target_if_tdls_set_offchan_mode(struct wlan_objmgr_psoc *psoc, + struct tdls_channel_switch_params *params); + +/** + * target_if_tdls_set_uapsd() - lmac handler to set uapsd auto trigger cmd + * @psoc: psoc object + * @params: upasd parameters + * + * This function sets the trigger + * uapsd params such as service interval, delay interval + * and suspend interval which will be used by the firmware + * to send trigger frames periodically when there is no + * traffic on the transmit side. + * + * Return: QDF_STATUS + */ +QDF_STATUS +target_if_tdls_set_uapsd(struct wlan_objmgr_psoc *psoc, + struct sta_uapsd_trig_params *params); + +/** + * target_if_tdls_register_event_handler() - lmac handler to register tdls event + * handler + * @psoc : psoc object + * @arg: argument passed to lmac + * + * Return: QDF_STATUS + */ +QDF_STATUS +target_if_tdls_register_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg); + +/** + * target_if_tdls_unregister_event_handler() - lmac handler to unregister tdls + * event handler + * @psoc : psoc object + * @arg: argument passed to lmac + * + * Return: QDF_STATUS + */ +QDF_STATUS +target_if_tdls_unregister_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg); + +/** + * target_if_tdls_register_tx_ops() - lmac handler to register tdls tx ops + * callback functions + * @tx_ops: wlan_lmac_if_tx_ops object + * + * Return: QDF_STATUS + */ +QDF_STATUS +target_if_tdls_register_tx_ops(struct wlan_lmac_if_tx_ops *tx_ops); +#endif diff --git a/components/target_if/tdls/src/target_if_tdls.c b/components/target_if/tdls/src/target_if_tdls.c new file mode 100644 index 000000000000..6adb2622319e --- /dev/null +++ b/components/target_if/tdls/src/target_if_tdls.c @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: offload lmac interface APIs for tdls + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline struct wlan_lmac_if_tdls_rx_ops * +target_if_tdls_get_rx_ops(struct wlan_objmgr_psoc *psoc) +{ + return &psoc->soc_cb.rx_ops.tdls_rx_ops; +} + +static int +target_if_tdls_event_handler(ol_scn_t scn, uint8_t *data, uint32_t datalen) +{ + struct wlan_objmgr_psoc *psoc; + struct wmi_unified *wmi_handle; + struct wlan_lmac_if_tdls_rx_ops *tdls_rx_ops; + struct tdls_event_info info; + QDF_STATUS status; + + if (!scn || !data) { + target_if_err("scn: 0x%pK, data: 0x%pK", scn, data); + return -EINVAL; + } + psoc = target_if_get_psoc_from_scn_hdl(scn); + if (!psoc) { + target_if_err("null psoc"); + return -EINVAL; + } + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + + if (!wmi_handle) { + target_if_err("null wmi_handle"); + return -EINVAL; + } + + if (wmi_extract_vdev_tdls_ev_param(wmi_handle, data, &info)) { + target_if_err("Failed to extract wmi tdls event"); + return -EINVAL; + } + + tdls_rx_ops = target_if_tdls_get_rx_ops(psoc); + if (tdls_rx_ops && tdls_rx_ops->tdls_ev_handler) { + status = tdls_rx_ops->tdls_ev_handler(psoc, &info); + if (QDF_IS_STATUS_ERROR(status)) { + target_if_err("fail to handle tdls event"); + return -EINVAL; + } + } + + return 0; +} + +QDF_STATUS +target_if_tdls_update_fw_state(struct wlan_objmgr_psoc *psoc, + struct tdls_info *param) +{ + QDF_STATUS status; + uint8_t tdls_state; + struct wmi_unified *wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid WMI handle"); + return QDF_STATUS_E_FAILURE; + } + + if (TDLS_SUPPORT_EXP_TRIG_ONLY == param->tdls_state) + tdls_state = WMI_TDLS_ENABLE_PASSIVE; + else if (TDLS_SUPPORT_IMP_MODE == param->tdls_state || + TDLS_SUPPORT_EXT_CONTROL == param->tdls_state) + tdls_state = WMI_TDLS_ENABLE_CONNECTION_TRACKER_IN_HOST; + else + tdls_state = WMI_TDLS_DISABLE; + + status = wmi_unified_update_fw_tdls_state_cmd(wmi_handle, + param, tdls_state); + + target_if_debug("vdev_id %d", param->vdev_id); + return status; +} + +QDF_STATUS +target_if_tdls_update_peer_state(struct wlan_objmgr_psoc *psoc, + struct tdls_peer_update_state *peer_params) +{ + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +target_if_tdls_set_offchan_mode(struct wlan_objmgr_psoc *psoc, + struct tdls_channel_switch_params *params) +{ + QDF_STATUS status; + struct wmi_unified *wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid WMI handle"); + return QDF_STATUS_E_FAILURE; + } + status = wmi_unified_set_tdls_offchan_mode_cmd(wmi_handle, + params); + + return status; +} + +QDF_STATUS +target_if_tdls_set_uapsd(struct wlan_objmgr_psoc *psoc, + struct sta_uapsd_trig_params *params) +{ + QDF_STATUS ret; + struct wmi_unified *wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("Invalid WMI handle"); + return QDF_STATUS_E_FAILURE; + } + if (!wmi_service_enabled(wmi_handle, + wmi_sta_uapsd_basic_auto_trig) || + !wmi_service_enabled(wmi_handle, + wmi_sta_uapsd_var_auto_trig)) { + target_if_debug("Trigger uapsd is not supported vdev id %d", + params->vdevid); + return QDF_STATUS_SUCCESS; + } + ret = wmi_unified_set_sta_uapsd_auto_trig_cmd(wmi_handle, + params); + + if (QDF_IS_STATUS_ERROR(ret)) + target_if_err("Failed to send set uapsd param ret = %d", ret); + + return ret; +} + +QDF_STATUS +target_if_tdls_register_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg) +{ + struct wmi_unified *wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("null wmi_handle"); + return QDF_STATUS_E_INVAL; + } + return wmi_unified_register_event(wmi_handle, + wmi_tdls_peer_event_id, + target_if_tdls_event_handler); +} + +QDF_STATUS +target_if_tdls_unregister_event_handler(struct wlan_objmgr_psoc *psoc, + void *arg) +{ + struct wmi_unified *wmi_handle; + + wmi_handle = get_wmi_unified_hdl_from_psoc(psoc); + if (!wmi_handle) { + target_if_err("null wmi_handle"); + return QDF_STATUS_E_INVAL; + } + return wmi_unified_unregister_event(wmi_handle, + wmi_tdls_peer_event_id); +} + +QDF_STATUS +target_if_tdls_register_tx_ops(struct wlan_lmac_if_tx_ops *tx_ops) +{ + struct wlan_lmac_if_tdls_tx_ops *tdls_txops; + + tdls_txops = &tx_ops->tdls_tx_ops; + + tdls_txops->update_fw_state = target_if_tdls_update_fw_state; + tdls_txops->update_peer_state = target_if_tdls_update_peer_state; + tdls_txops->set_offchan_mode = target_if_tdls_set_offchan_mode; + tdls_txops->tdls_reg_ev_handler = target_if_tdls_register_event_handler; + tdls_txops->tdls_unreg_ev_handler = + target_if_tdls_unregister_event_handler; + tdls_txops->tdls_set_uapsd = target_if_tdls_set_uapsd; + + return QDF_STATUS_SUCCESS; +} diff --git a/components/tdls/core/src/wlan_tdls_cmds_process.c b/components/tdls/core/src/wlan_tdls_cmds_process.c new file mode 100644 index 000000000000..a27c040ef63a --- /dev/null +++ b/components/tdls/core/src/wlan_tdls_cmds_process.c @@ -0,0 +1,2431 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_cmds_process.c + * + * TDLS north bound commands implementation + */ +#include +#include +#include "wlan_tdls_main.h" +#include "wlan_tdls_peer.h" +#include "wlan_tdls_ct.h" +#include "wlan_tdls_mgmt.h" +#include "wlan_tdls_cmds_process.h" +#include "wlan_tdls_tgt_api.h" +#include "wlan_policy_mgr_api.h" + +static uint16_t tdls_get_connected_peer(struct tdls_soc_priv_obj *soc_obj) +{ + return soc_obj->connected_peer_count; +} + +/** + * tdls_decrement_peer_count() - decrement connected TDLS peer counter + * @soc_obj: TDLS soc object + * + * Used in scheduler thread context, no lock needed. + * + * Return: None. + */ +void tdls_decrement_peer_count(struct tdls_soc_priv_obj *soc_obj) +{ + if (soc_obj->connected_peer_count) + soc_obj->connected_peer_count--; + + tdls_debug("Connected peer count %d", soc_obj->connected_peer_count); +} + +/** + * tdls_increment_peer_count() - increment connected TDLS peer counter + * @soc_obj: TDLS soc object + * + * Used in scheduler thread context, no lock needed. + * + * Return: None. + */ +static void tdls_increment_peer_count(struct tdls_soc_priv_obj *soc_obj) +{ + soc_obj->connected_peer_count++; + tdls_debug("Connected peer count %d", soc_obj->connected_peer_count); +} + +/** + * tdls_validate_current_mode() - check current TDL mode + * @soc_obj: TDLS soc object + * + * Return: QDF_STATUS_SUCCESS if TDLS enabled, other for disabled + */ +static QDF_STATUS tdls_validate_current_mode(struct tdls_soc_priv_obj *soc_obj) +{ + if (soc_obj->tdls_current_mode == TDLS_SUPPORT_DISABLED || + soc_obj->tdls_current_mode == TDLS_SUPPORT_SUSPENDED) { + tdls_err("TDLS mode disabled OR not enabled, current mode %d", + soc_obj->tdls_current_mode); + return QDF_STATUS_E_NOSUPPORT; + } + return QDF_STATUS_SUCCESS; +} + +static char *tdls_get_ser_cmd_str(enum wlan_serialization_cmd_type type) +{ + switch (type) { + case WLAN_SER_CMD_TDLS_ADD_PEER: + return "TDLS_ADD_PEER_CMD"; + case WLAN_SER_CMD_TDLS_DEL_PEER: + return "TDLS_DEL_PEER_CMD"; + case WLAN_SER_CMD_TDLS_SEND_MGMT: + return "TDLS_SEND_MGMT_CMD"; + default: + return "UNKNOWN"; + } +} + +void +tdls_release_serialization_command(struct wlan_objmgr_vdev *vdev, + enum wlan_serialization_cmd_type type) +{ + struct wlan_serialization_queued_cmd_info cmd = {0}; + + cmd.cmd_type = type; + cmd.cmd_id = 0; + cmd.vdev = vdev; + + tdls_debug("release %s", tdls_get_ser_cmd_str(type)); + /* Inform serialization for command completion */ + wlan_serialization_remove_cmd(&cmd); +} + +/** + * tdls_pe_add_peer() - send TDLS add peer request to PE + * @req: TDL add peer request + * + * Return: QDF_STATUS_SUCCESS for success; other values if failed + */ +static QDF_STATUS tdls_pe_add_peer(struct tdls_add_peer_request *req) +{ + struct tdls_add_sta_req *addstareq; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_peer *peer; + struct tdls_soc_priv_obj *soc_obj; + struct scheduler_msg msg = {0,}; + QDF_STATUS status; + + addstareq = qdf_mem_malloc(sizeof(*addstareq)); + if (!addstareq) { + tdls_err("allocate failed"); + return QDF_STATUS_E_NOMEM; + } + vdev = req->vdev; + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (!soc_obj) { + tdls_err("NULL tdls soc object"); + status = QDF_STATUS_E_INVAL; + goto error; + } + + addstareq->tdls_oper = TDLS_OPER_ADD; + addstareq->transaction_id = 0; + + addstareq->session_id = wlan_vdev_get_id(vdev); + peer = wlan_vdev_get_bsspeer(vdev); + if (!peer) { + tdls_err("bss peer is NULL"); + status = QDF_STATUS_E_INVAL; + goto error; + } + status = wlan_objmgr_peer_try_get_ref(peer, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get bss peer"); + goto error; + } + wlan_peer_obj_lock(peer); + qdf_mem_copy(addstareq->bssid.bytes, + wlan_peer_get_macaddr(peer), QDF_MAC_ADDR_SIZE); + wlan_peer_obj_unlock(peer); + wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_NB_ID); + qdf_mem_copy(addstareq->peermac.bytes, req->add_peer_req.peer_addr, + QDF_MAC_ADDR_SIZE); + + tdls_debug("for " QDF_MAC_ADDR_STR, + QDF_MAC_ADDR_ARRAY(addstareq->peermac.bytes)); + msg.type = soc_obj->tdls_add_sta_req; + msg.bodyptr = addstareq; + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("fail to post pe msg to add peer"); + goto error; + } + return status; +error: + qdf_mem_free(addstareq); + return status; +} + +/** + * tdls_pe_del_peer() - send TDLS delete peer request to PE + * @req: TDLS delete peer request + * + * Return: QDF_STATUS_SUCCESS for success; other values if failed + */ +QDF_STATUS tdls_pe_del_peer(struct tdls_del_peer_request *req) +{ + struct tdls_del_sta_req *delstareq; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_peer *peer; + struct tdls_soc_priv_obj *soc_obj; + struct scheduler_msg msg = {0,}; + QDF_STATUS status; + + delstareq = qdf_mem_malloc(sizeof(*delstareq)); + if (!delstareq) { + tdls_err("allocate failed"); + return QDF_STATUS_E_NOMEM; + } + vdev = req->vdev; + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (!soc_obj) { + tdls_err("NULL tdls soc object"); + status = QDF_STATUS_E_INVAL; + goto error; + } + + delstareq->transaction_id = 0; + + delstareq->session_id = wlan_vdev_get_id(vdev); + peer = wlan_vdev_get_bsspeer(vdev); + if (!peer) { + tdls_err("bss peer is NULL"); + status = QDF_STATUS_E_INVAL; + goto error; + } + status = wlan_objmgr_peer_try_get_ref(peer, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get bss peer"); + goto error; + } + wlan_peer_obj_lock(peer); + qdf_mem_copy(delstareq->bssid.bytes, + wlan_peer_get_macaddr(peer), QDF_MAC_ADDR_SIZE); + wlan_peer_obj_unlock(peer); + wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_NB_ID); + qdf_mem_copy(delstareq->peermac.bytes, req->del_peer_req.peer_addr, + QDF_MAC_ADDR_SIZE); + + tdls_debug("for " QDF_MAC_ADDR_STR, + QDF_MAC_ADDR_ARRAY(delstareq->peermac.bytes)); + msg.type = soc_obj->tdls_del_sta_req; + msg.bodyptr = delstareq; + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("fail to post pe msg to del peer"); + goto error; + } + return status; +error: + qdf_mem_free(delstareq); + return status; +} + +/** + * tdls_pe_update_peer() - send TDLS update peer request to PE + * @req: TDLS update peer request + * + * Return: QDF_STATUS_SUCCESS for success; other values if failed + */ +static QDF_STATUS tdls_pe_update_peer(struct tdls_update_peer_request *req) +{ + struct tdls_add_sta_req *addstareq; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_peer *peer; + struct tdls_soc_priv_obj *soc_obj; + struct scheduler_msg msg = {0,}; + struct tdls_update_peer_params *update_peer; + QDF_STATUS status; + + addstareq = qdf_mem_malloc(sizeof(*addstareq)); + if (!addstareq) { + tdls_err("allocate failed"); + return QDF_STATUS_E_NOMEM; + } + vdev = req->vdev; + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (!soc_obj) { + tdls_err("NULL tdls soc object"); + status = QDF_STATUS_E_INVAL; + goto error; + } + update_peer = &req->update_peer_req; + + addstareq->tdls_oper = TDLS_OPER_UPDATE; + addstareq->transaction_id = 0; + + addstareq->session_id = wlan_vdev_get_id(vdev); + peer = wlan_vdev_get_bsspeer(vdev); + if (!peer) { + tdls_err("bss peer is NULL"); + status = QDF_STATUS_E_INVAL; + goto error; + } + status = wlan_objmgr_peer_try_get_ref(peer, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get bss peer"); + goto error; + } + wlan_peer_obj_lock(peer); + qdf_mem_copy(addstareq->bssid.bytes, + wlan_peer_get_macaddr(peer), QDF_MAC_ADDR_SIZE); + wlan_peer_obj_unlock(peer); + wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_NB_ID); + qdf_mem_copy(addstareq->peermac.bytes, update_peer->peer_addr, + QDF_MAC_ADDR_SIZE); + addstareq->capability = update_peer->capability; + addstareq->uapsd_queues = update_peer->uapsd_queues; + addstareq->max_sp = update_peer->max_sp; + + qdf_mem_copy(addstareq->extn_capability, + update_peer->extn_capability, WLAN_MAC_MAX_EXTN_CAP); + addstareq->htcap_present = update_peer->htcap_present; + qdf_mem_copy(&addstareq->ht_cap, + &update_peer->ht_cap, + sizeof(update_peer->ht_cap)); + addstareq->vhtcap_present = update_peer->vhtcap_present; + qdf_mem_copy(&addstareq->vht_cap, + &update_peer->vht_cap, + sizeof(update_peer->vht_cap)); + addstareq->supported_rates_length = update_peer->supported_rates_len; + qdf_mem_copy(&addstareq->supported_rates, + update_peer->supported_rates, + update_peer->supported_rates_len); + tdls_debug("for " QDF_MAC_ADDR_STR, + QDF_MAC_ADDR_ARRAY(addstareq->peermac.bytes)); + + msg.type = soc_obj->tdls_add_sta_req; + msg.bodyptr = addstareq; + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("fail to post pe msg to update peer"); + goto error; + } + return status; +error: + qdf_mem_free(addstareq); + return status; +} + +static QDF_STATUS +tdls_internal_add_peer_rsp(struct tdls_add_peer_request *req, + QDF_STATUS status) +{ + struct tdls_soc_priv_obj *soc_obj; + struct wlan_objmgr_vdev *vdev; + struct tdls_osif_indication ind; + QDF_STATUS ret; + + if (!req || !req->vdev) { + tdls_err("req: %pK", req); + return QDF_STATUS_E_INVAL; + } + vdev = req->vdev; + ret = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_SB_ID); + if (QDF_IS_STATUS_ERROR(ret)) { + tdls_err("can't get vdev object"); + return ret; + } + + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (soc_obj && soc_obj->tdls_event_cb) { + ind.vdev = vdev; + ind.status = status; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_ADD_PEER, &ind); + } + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS +tdls_internal_update_peer_rsp(struct tdls_update_peer_request *req, + QDF_STATUS status) +{ + struct tdls_soc_priv_obj *soc_obj; + struct tdls_osif_indication ind; + struct wlan_objmgr_vdev *vdev; + QDF_STATUS ret; + + if (!req || !req->vdev) { + tdls_err("req: %pK", req); + return QDF_STATUS_E_INVAL; + } + vdev = req->vdev; + ret = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_SB_ID); + if (QDF_IS_STATUS_ERROR(ret)) { + tdls_err("can't get vdev object"); + return ret; + } + + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (soc_obj && soc_obj->tdls_event_cb) { + ind.vdev = vdev; + ind.status = status; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_ADD_PEER, &ind); + } + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS tdls_internal_del_peer_rsp(struct tdls_oper_request *req) +{ + struct tdls_soc_priv_obj *soc_obj; + struct tdls_osif_indication ind; + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status; + + if (!req || !req->vdev) { + tdls_err("req: %pK", req); + return QDF_STATUS_E_INVAL; + } + vdev = req->vdev; + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev object"); + return status; + } + + soc_obj = wlan_vdev_get_tdls_soc_obj(req->vdev); + if (soc_obj && soc_obj->tdls_event_cb) { + ind.vdev = req->vdev; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_DEL_PEER, &ind); + } + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS tdls_activate_add_peer(struct tdls_add_peer_request *req) +{ + QDF_STATUS status; + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + struct tdls_peer *peer; + uint16_t curr_tdls_peers; + const uint8_t *mac; + struct tdls_osif_indication ind; + + if (!req->vdev) { + tdls_err("vdev null when add tdls peer"); + QDF_ASSERT(0); + return QDF_STATUS_E_NULL_VALUE; + } + + mac = req->add_peer_req.peer_addr; + soc_obj = wlan_vdev_get_tdls_soc_obj(req->vdev); + vdev_obj = wlan_vdev_get_tdls_vdev_obj(req->vdev); + + if (!soc_obj || !vdev_obj) { + tdls_err("soc_obj: %pK, vdev_obj: %pK", soc_obj, vdev_obj); + return QDF_STATUS_E_INVAL; + } + status = tdls_validate_current_mode(soc_obj); + if (QDF_IS_STATUS_ERROR(status)) + goto addrsp; + + peer = tdls_get_peer(vdev_obj, mac); + if (!peer) { + tdls_err("peer: " QDF_MAC_ADDR_STR " not exist. invalid", + QDF_MAC_ADDR_ARRAY(mac)); + status = QDF_STATUS_E_INVAL; + goto addrsp; + } + + /* in add station, we accept existing valid sta_id if there is */ + if ((peer->link_status > TDLS_LINK_CONNECTING) || + (TDLS_STA_INDEX_CHECK((peer->sta_id)))) { + tdls_notice("link_status %d sta_id %d add peer ignored", + peer->link_status, peer->sta_id); + status = QDF_STATUS_SUCCESS; + goto addrsp; + } + + /* when others are on-going, we want to change link_status to idle */ + if (tdls_is_progress(vdev_obj, mac, true)) { + tdls_notice(QDF_MAC_ADDR_STR " TDLS setuping. Req declined.", + QDF_MAC_ADDR_ARRAY(mac)); + status = QDF_STATUS_E_PERM; + goto setlink; + } + + /* first to check if we reached to maximum supported TDLS peer. */ + curr_tdls_peers = tdls_get_connected_peer(soc_obj); + if (soc_obj->max_num_tdls_sta <= curr_tdls_peers) { + tdls_err(QDF_MAC_ADDR_STR + " Request declined. Current %d, Max allowed %d.", + QDF_MAC_ADDR_ARRAY(mac), curr_tdls_peers, + soc_obj->max_num_tdls_sta); + status = QDF_STATUS_E_PERM; + goto setlink; + } + + tdls_set_peer_link_status(peer, + TDLS_LINK_CONNECTING, TDLS_LINK_SUCCESS); + + status = tdls_pe_add_peer(req); + if (QDF_IS_STATUS_ERROR(status)) + goto setlink; + + return QDF_STATUS_SUCCESS; + +setlink: + tdls_set_link_status(vdev_obj, mac, TDLS_LINK_IDLE, + TDLS_LINK_UNSPECIFIED); +addrsp: + if (soc_obj->tdls_event_cb) { + ind.status = status; + ind.vdev = req->vdev; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_ADD_PEER, &ind); + } + + return QDF_STATUS_E_PERM; +} + +static QDF_STATUS +tdls_add_peer_serialize_callback(struct wlan_serialization_command *cmd, + enum wlan_serialization_cb_reason reason) +{ + struct tdls_add_peer_request *req; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!cmd || !cmd->umac_cmd) { + tdls_err("cmd: %pK, reason: %d", cmd, reason); + return QDF_STATUS_E_NULL_VALUE; + } + + req = cmd->umac_cmd; + tdls_debug("reason: %d, req %pK", reason, req); + + switch (reason) { + case WLAN_SER_CB_ACTIVATE_CMD: + /* command moved to active list + */ + status = tdls_activate_add_peer(req); + break; + + case WLAN_SER_CB_CANCEL_CMD: + /* command removed from pending list. + * notify os interface the status + */ + status = tdls_internal_add_peer_rsp(req, QDF_STATUS_E_FAILURE); + break; + + case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT: + /* active command time out. */ + status = tdls_internal_add_peer_rsp(req, QDF_STATUS_E_FAILURE); + break; + + case WLAN_SER_CB_RELEASE_MEM_CMD: + /* command successfully completed. + * release memory & vdev reference count + */ + wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(req); + break; + + default: + /* Do nothing but logging */ + QDF_ASSERT(0); + status = QDF_STATUS_E_INVAL; + break; + } + + return status; +} + +void tdls_reset_nss(struct tdls_soc_priv_obj *tdls_soc, + uint8_t action_code) +{ + if (!tdls_soc) + return; + + if (TDLS_TEARDOWN != action_code || + !tdls_soc->tdls_nss_switch_in_progress) + return; + + if (tdls_soc->tdls_teardown_peers_cnt != 0) + tdls_soc->tdls_teardown_peers_cnt--; + if (tdls_soc->tdls_teardown_peers_cnt == 0) { + if (tdls_soc->tdls_nss_transition_mode == + TDLS_NSS_TRANSITION_S_1x1_to_2x2) { + /* TDLS NSS switch is fully completed, so + * reset the flags. + */ + tdls_notice("TDLS NSS switch is fully completed"); + tdls_soc->tdls_nss_switch_in_progress = false; + tdls_soc->tdls_nss_teardown_complete = false; + } else { + /* TDLS NSS switch is not yet completed, but + * tdls teardown is completed for all the + * peers. + */ + tdls_notice("teardown done & NSS switch in progress"); + tdls_soc->tdls_nss_teardown_complete = true; + } + tdls_soc->tdls_nss_transition_mode = + TDLS_NSS_TRANSITION_S_UNKNOWN; + } + +} + +/** + * tdls_set_cap() - set TDLS capability type + * @tdls_vdev: tdls vdev object + * @mac: peer mac address + * @cap: TDLS capability type + * + * Return: 0 if successful or negative errno otherwise + */ +int tdls_set_cap(struct tdls_vdev_priv_obj *tdls_vdev, const uint8_t *mac, + enum tdls_peer_capab cap) +{ + struct tdls_peer *curr_peer; + + curr_peer = tdls_get_peer(tdls_vdev, mac); + if (curr_peer == NULL) { + tdls_err("curr_peer is NULL"); + return -EINVAL; + } + + curr_peer->tdls_support = cap; + return 0; +} + +static int tdls_validate_setup_frames(struct tdls_soc_priv_obj *tdls_soc, + struct tdls_validate_action_req *tdls_validate) +{ + /* supplicant still sends tdls_mgmt(SETUP_REQ) + * even after we return error code at + * 'add_station()'. Hence we have this check + * again in addition to add_station(). Anyway, + * there is no harm to double-check. + */ + if (TDLS_SETUP_REQUEST == tdls_validate->action_code) { + tdls_err(QDF_MAC_ADDR_STR " TDLS Max peer already connected. action (%d) declined. Num of peers (%d), Max allowed (%d).", + QDF_MAC_ADDR_ARRAY(tdls_validate->peer_mac), + tdls_validate->action_code, + tdls_soc->connected_peer_count, + tdls_soc->max_num_tdls_sta); + return -EINVAL; + } + /* maximum reached. tweak to send + * error code to peer and return error + * code to supplicant + */ + tdls_validate->status_code = QDF_STATUS_E_RESOURCES; + tdls_err(QDF_MAC_ADDR_STR " TDLS Max peer already connected, send response status (%d). Num of peers (%d), Max allowed (%d).", + QDF_MAC_ADDR_ARRAY(tdls_validate->peer_mac), + tdls_validate->action_code, + tdls_soc->connected_peer_count, + tdls_soc->max_num_tdls_sta); + + tdls_validate->max_sta_failed = -EPERM; + return 0; +} + +int tdls_validate_mgmt_request(struct tdls_action_frame_request *tdls_mgmt_req) +{ + struct tdls_vdev_priv_obj *tdls_vdev; + struct tdls_soc_priv_obj *tdls_soc; + struct tdls_peer *curr_peer; + struct tdls_peer *temp_peer; + QDF_STATUS status; + uint8_t vdev_id; + + struct tdls_validate_action_req *tdls_validate = + tdls_mgmt_req->chk_frame; + + if (!tdls_validate || !tdls_validate->vdev) + return -EINVAL; + + if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(tdls_validate->vdev, + &tdls_vdev, + &tdls_soc)) + return -ENOTSUPP; + + /* + * STA or P2P client should be connected and authenticated before + * sending any TDLS frames + */ + if (!tdls_is_vdev_connected(tdls_validate->vdev) || + !tdls_is_vdev_authenticated(tdls_validate->vdev)) { + tdls_err("STA is not connected or not authenticated."); + return -EAGAIN; + } + + /* other than teardown frame, mgmt frames are not sent if disabled */ + if (TDLS_TEARDOWN != tdls_validate->action_code) { + if (!tdls_check_is_tdls_allowed(tdls_validate->vdev)) { + tdls_err("TDLS not allowed, reject MGMT, action = %d", + tdls_validate->action_code); + return -EPERM; + } + /* if tdls_mode is disabled, then decline the peer's request */ + if (TDLS_SUPPORT_DISABLED == tdls_soc->tdls_current_mode || + TDLS_SUPPORT_SUSPENDED == tdls_soc->tdls_current_mode) { + tdls_notice(QDF_MAC_ADDR_STR + " TDLS mode is disabled. action %d declined.", + QDF_MAC_ADDR_ARRAY(tdls_validate->peer_mac), + tdls_validate->action_code); + return -ENOTSUPP; + } + if (tdls_soc->tdls_nss_switch_in_progress) { + tdls_err("nss switch in progress, action %d declined " + QDF_MAC_ADDR_STR, + tdls_validate->action_code, + QDF_MAC_ADDR_ARRAY(tdls_validate->peer_mac)); + return -EAGAIN; + } + } + + if (TDLS_IS_SETUP_ACTION(tdls_validate->action_code)) { + if (NULL != tdls_is_progress(tdls_vdev, + tdls_validate->peer_mac, true)) { + tdls_err("setup is ongoing. action %d declined for " + QDF_MAC_ADDR_STR, + tdls_validate->action_code, + QDF_MAC_ADDR_ARRAY(tdls_validate->peer_mac)); + return -EPERM; + } + } + + /* call hdd_wmm_is_acm_allowed() */ + vdev_id = wlan_vdev_get_id(tdls_validate->vdev); + if (!tdls_soc->tdls_wmm_cb(vdev_id)) { + tdls_debug("admission ctrl set to VI, send the frame with least AC (BK) for action %d", + tdls_validate->action_code); + tdls_mgmt_req->use_default_ac = false; + } else { + tdls_mgmt_req->use_default_ac = true; + } + + if (TDLS_SETUP_REQUEST == tdls_validate->action_code || + TDLS_SETUP_RESPONSE == tdls_validate->action_code) { + if (tdls_soc->max_num_tdls_sta <= + tdls_soc->connected_peer_count) { + status = tdls_validate_setup_frames(tdls_soc, + tdls_validate); + if (QDF_STATUS_SUCCESS != status) + return status; + /* fall through to send setup resp + * with failure status code + */ + } else { + curr_peer = + tdls_find_peer(tdls_vdev, + tdls_validate->peer_mac); + if (curr_peer) { + if (TDLS_IS_LINK_CONNECTED(curr_peer)) { + tdls_err(QDF_MAC_ADDR_STR " already connected action %d declined.", + QDF_MAC_ADDR_ARRAY( + tdls_validate->peer_mac), + tdls_validate->action_code); + + return -EPERM; + } + } + } + } + + tdls_notice("tdls_mgmt" QDF_MAC_ADDR_STR " action %d, dialog_token %d status %d, len = %zu", + QDF_MAC_ADDR_ARRAY(tdls_validate->peer_mac), + tdls_validate->action_code, tdls_validate->dialog_token, + tdls_validate->status_code, tdls_validate->len); + + /*Except teardown responder will not be used so just make 0 */ + tdls_validate->responder = 0; + if (TDLS_TEARDOWN == tdls_validate->action_code) { + temp_peer = tdls_find_peer(tdls_vdev, tdls_validate->peer_mac); + if (!temp_peer) { + tdls_err(QDF_MAC_ADDR_STR " peer doesn't exist", + QDF_MAC_ADDR_ARRAY( + tdls_validate->peer_mac)); + return -EPERM; + } + + if (TDLS_IS_LINK_CONNECTED(temp_peer)) + tdls_validate->responder = temp_peer->is_responder; + else { + tdls_err(QDF_MAC_ADDR_STR " peer doesn't exist or not connected %d dialog_token %d status %d, tdls_validate->len = %zu", + QDF_MAC_ADDR_ARRAY(tdls_validate->peer_mac), + temp_peer->link_status, + tdls_validate->dialog_token, + tdls_validate->status_code, + tdls_validate->len); + return -EPERM; + } + } + + /* For explicit trigger of DIS_REQ come out of BMPS for + * successfully receiving DIS_RSP from peer. + */ + if ((TDLS_SETUP_RESPONSE == tdls_validate->action_code) || + (TDLS_SETUP_CONFIRM == tdls_validate->action_code) || + (TDLS_DISCOVERY_RESPONSE == tdls_validate->action_code) || + (TDLS_DISCOVERY_REQUEST == tdls_validate->action_code)) { + /* Fw will take care if PS offload is enabled. */ + if (TDLS_DISCOVERY_REQUEST != tdls_validate->action_code) + tdls_set_cap(tdls_vdev, tdls_validate->peer_mac, + TDLS_CAP_SUPPORTED); + } + return 0; +} + +QDF_STATUS tdls_process_add_peer(struct tdls_add_peer_request *req) +{ + struct wlan_serialization_command cmd = {0,}; + enum wlan_serialization_status ser_cmd_status; + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!req || !req->vdev) { + tdls_err("req: %pK", req); + status = QDF_STATUS_E_INVAL; + goto error; + } + vdev = req->vdev; + cmd.cmd_type = WLAN_SER_CMD_TDLS_ADD_PEER; + cmd.cmd_id = 0; + cmd.cmd_cb = (wlan_serialization_cmd_callback) + tdls_add_peer_serialize_callback; + cmd.umac_cmd = req; + cmd.source = WLAN_UMAC_COMP_TDLS; + cmd.is_high_priority = false; + cmd.cmd_timeout_duration = TDLS_DEFAULT_SERIALIZE_CMD_TIMEOUT; + cmd.vdev = vdev; + cmd.is_blocking = true; + + ser_cmd_status = wlan_serialization_request(&cmd); + tdls_debug("req: 0x%pK wlan_serialization_request status:%d", req, + ser_cmd_status); + + switch (ser_cmd_status) { + case WLAN_SER_CMD_PENDING: + /* command moved to pending list. Do nothing */ + break; + case WLAN_SER_CMD_ACTIVE: + /* command moved to active list. Do nothing */ + break; + case WLAN_SER_CMD_DENIED_LIST_FULL: + case WLAN_SER_CMD_DENIED_RULES_FAILED: + case WLAN_SER_CMD_DENIED_UNSPECIFIED: + /* notify os interface about internal error*/ + status = tdls_internal_add_peer_rsp(req, QDF_STATUS_E_FAILURE); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + /* cmd can't be serviced. + * release tdls_add_peer_request memory + */ + qdf_mem_free(req); + break; + default: + QDF_ASSERT(0); + status = QDF_STATUS_E_INVAL; + goto error; + } + + return status; +error: + status = tdls_internal_add_peer_rsp(req, QDF_STATUS_E_FAILURE); + qdf_mem_free(req); + return status; +} + +static QDF_STATUS +tdls_activate_update_peer(struct tdls_update_peer_request *req) +{ + QDF_STATUS status; + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + struct wlan_objmgr_vdev *vdev; + struct tdls_peer *curr_peer; + uint16_t curr_tdls_peers; + const uint8_t *mac; + struct tdls_update_peer_params *update_peer; + struct tdls_osif_indication ind; + + if (!req->vdev) { + tdls_err("vdev object NULL when add TDLS peer"); + QDF_ASSERT(0); + return QDF_STATUS_E_NULL_VALUE; + } + + mac = req->update_peer_req.peer_addr; + vdev = req->vdev; + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + if (!soc_obj || !vdev_obj) { + tdls_err("soc_obj: %pK, vdev_obj: %pK", soc_obj, vdev_obj); + return QDF_STATUS_E_INVAL; + } + + status = tdls_validate_current_mode(soc_obj); + if (QDF_IS_STATUS_ERROR(status)) + goto updatersp; + + curr_peer = tdls_get_peer(vdev_obj, mac); + if (!curr_peer) { + tdls_err(QDF_MAC_ADDR_STR " not exist. return invalid", + QDF_MAC_ADDR_ARRAY(mac)); + status = QDF_STATUS_E_INVAL; + goto updatersp; + } + + /* in change station, we accept only when sta_id is valid */ + if (curr_peer->link_status > TDLS_LINK_CONNECTING || + !(TDLS_STA_INDEX_CHECK(curr_peer->sta_id))) { + tdls_err(QDF_MAC_ADDR_STR " link %d. sta %d. update peer %s", + QDF_MAC_ADDR_ARRAY(mac), curr_peer->link_status, + curr_peer->sta_id, + (TDLS_STA_INDEX_CHECK(curr_peer->sta_id)) ? "ignored" + : "declined"); + status = (TDLS_STA_INDEX_CHECK(curr_peer->sta_id)) ? + QDF_STATUS_SUCCESS : QDF_STATUS_E_PERM; + goto updatersp; + } + + /* when others are on-going, we want to change link_status to idle */ + if (tdls_is_progress(vdev_obj, mac, true)) { + tdls_notice(QDF_MAC_ADDR_STR " TDLS setuping. Req declined.", + QDF_MAC_ADDR_ARRAY(mac)); + status = QDF_STATUS_E_PERM; + goto setlink; + } + + curr_tdls_peers = tdls_get_connected_peer(soc_obj); + if (soc_obj->max_num_tdls_sta <= curr_tdls_peers) { + tdls_err(QDF_MAC_ADDR_STR + " Request declined. Current: %d, Max allowed: %d.", + QDF_MAC_ADDR_ARRAY(mac), curr_tdls_peers, + soc_obj->max_num_tdls_sta); + status = QDF_STATUS_E_PERM; + goto setlink; + } + update_peer = &req->update_peer_req; + + if (update_peer->htcap_present) + curr_peer->spatial_streams = update_peer->ht_cap.mcsset[1]; + + tdls_set_peer_caps(vdev_obj, mac, &req->update_peer_req); + status = tdls_pe_update_peer(req); + if (QDF_IS_STATUS_ERROR(status)) + goto setlink; + + return QDF_STATUS_SUCCESS; + +setlink: + tdls_set_link_status(vdev_obj, mac, TDLS_LINK_IDLE, + TDLS_LINK_UNSPECIFIED); +updatersp: + if (soc_obj->tdls_event_cb) { + ind.status = status; + ind.vdev = vdev; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_ADD_PEER, &ind); + } + + return QDF_STATUS_E_PERM; +} + +static QDF_STATUS +tdls_update_peer_serialize_callback(struct wlan_serialization_command *cmd, + enum wlan_serialization_cb_reason reason) +{ + struct tdls_update_peer_request *req; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!cmd || !cmd->umac_cmd) { + tdls_err("cmd: %pK, reason: %d", cmd, reason); + return QDF_STATUS_E_NULL_VALUE; + } + + req = cmd->umac_cmd; + tdls_debug("reason: %d, req %pK", reason, req); + + switch (reason) { + case WLAN_SER_CB_ACTIVATE_CMD: + /* command moved to active list + */ + status = tdls_activate_update_peer(req); + break; + + case WLAN_SER_CB_CANCEL_CMD: + /* command removed from pending list. + * notify os interface the status + */ + status = tdls_internal_update_peer_rsp(req, + QDF_STATUS_E_FAILURE); + break; + + case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT: + /* active command time out. */ + status = tdls_internal_update_peer_rsp(req, + QDF_STATUS_E_FAILURE); + break; + + case WLAN_SER_CB_RELEASE_MEM_CMD: + /* command successfully completed. + * release memory & release reference count + */ + wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(req); + break; + + default: + /* Do nothing but logging */ + QDF_ASSERT(0); + status = QDF_STATUS_E_INVAL; + break; + } + + return status; +} + +QDF_STATUS tdls_process_update_peer(struct tdls_update_peer_request *req) +{ + struct wlan_serialization_command cmd = {0,}; + enum wlan_serialization_status ser_cmd_status; + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!req || !req->vdev) { + tdls_err("req: %pK", req); + status = QDF_STATUS_E_FAILURE; + goto error; + } + + vdev = req->vdev; + cmd.cmd_type = WLAN_SER_CMD_TDLS_ADD_PEER; + cmd.cmd_id = 0; + cmd.cmd_cb = (wlan_serialization_cmd_callback) + tdls_update_peer_serialize_callback; + cmd.umac_cmd = req; + cmd.source = WLAN_UMAC_COMP_TDLS; + cmd.is_high_priority = false; + cmd.cmd_timeout_duration = TDLS_DEFAULT_SERIALIZE_CMD_TIMEOUT; + cmd.vdev = req->vdev; + cmd.is_blocking = true; + + ser_cmd_status = wlan_serialization_request(&cmd); + tdls_debug("req: 0x%pK wlan_serialization_request status:%d", req, + ser_cmd_status); + + switch (ser_cmd_status) { + case WLAN_SER_CMD_PENDING: + /* command moved to pending list. Do nothing */ + break; + case WLAN_SER_CMD_ACTIVE: + /* command moved to active list. Do nothing */ + break; + case WLAN_SER_CMD_DENIED_LIST_FULL: + case WLAN_SER_CMD_DENIED_RULES_FAILED: + case WLAN_SER_CMD_DENIED_UNSPECIFIED: + /* notify os interface about internal error*/ + status = tdls_internal_update_peer_rsp(req, + QDF_STATUS_E_FAILURE); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + /* cmd can't be serviced. + * release tdls_add_peer_request memory + */ + qdf_mem_free(req); + break; + default: + QDF_ASSERT(0); + status = QDF_STATUS_E_INVAL; + break; + } + + return status; +error: + status = tdls_internal_update_peer_rsp(req, QDF_STATUS_E_FAILURE); + qdf_mem_free(req); + return status; +} + +static QDF_STATUS tdls_activate_del_peer(struct tdls_oper_request *req) +{ + struct tdls_del_peer_request request = {0,}; + + request.vdev = req->vdev; + request.del_peer_req.peer_addr = req->peer_addr; + + return tdls_pe_del_peer(&request); +} + +static QDF_STATUS +tdls_del_peer_serialize_callback(struct wlan_serialization_command *cmd, + enum wlan_serialization_cb_reason reason) +{ + struct tdls_oper_request *req; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!cmd || !cmd->umac_cmd) { + tdls_err("cmd: %pK, reason: %d", cmd, reason); + return QDF_STATUS_E_NULL_VALUE; + } + + req = cmd->umac_cmd; + tdls_debug("reason: %d, req %pK", reason, req); + + switch (reason) { + case WLAN_SER_CB_ACTIVATE_CMD: + /* command moved to active list + */ + status = tdls_activate_del_peer(req); + break; + + case WLAN_SER_CB_CANCEL_CMD: + /* command removed from pending list. + * notify os interface the status + */ + status = tdls_internal_del_peer_rsp(req); + break; + + case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT: + /* active command time out. */ + status = tdls_internal_del_peer_rsp(req); + break; + + case WLAN_SER_CB_RELEASE_MEM_CMD: + /* command successfully completed. + * release memory & vdev reference count + */ + wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(req); + break; + + default: + /* Do nothing but logging */ + QDF_ASSERT(0); + status = QDF_STATUS_E_INVAL; + break; + } + + return status; +} + +QDF_STATUS tdls_process_del_peer(struct tdls_oper_request *req) +{ + struct wlan_serialization_command cmd = {0,}; + enum wlan_serialization_status ser_cmd_status; + struct wlan_objmgr_vdev *vdev; + struct tdls_vdev_priv_obj *vdev_obj; + struct tdls_soc_priv_obj *soc_obj; + uint8_t *mac; + struct tdls_peer *peer; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!req || !req->vdev) { + tdls_err("req: %pK", req); + status = QDF_STATUS_E_INVAL; + goto free_req; + } + + vdev = req->vdev; + + /* vdev reference cnt is acquired in ucfg_tdls_oper */ + vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + + if (!vdev_obj || !soc_obj) { + tdls_err("tdls vdev_obj: %pK soc_obj: %pK", vdev_obj, soc_obj); + status = QDF_STATUS_E_NULL_VALUE; + goto error; + } + + mac = req->peer_addr; + peer = tdls_find_peer(vdev_obj, mac); + if (!peer) { + tdls_err(QDF_MAC_ADDR_STR + " not found, ignore NL80211_TDLS_ENABLE_LINK", + QDF_MAC_ADDR_ARRAY(mac)); + status = QDF_STATUS_E_INVAL; + goto error; + } + + if (soc_obj->tdls_dp_vdev_update) + soc_obj->tdls_dp_vdev_update(&soc_obj->soc, + peer->sta_id, + soc_obj->tdls_update_dp_vdev_flags, + false); + + cmd.cmd_type = WLAN_SER_CMD_TDLS_DEL_PEER; + cmd.cmd_id = 0; + cmd.cmd_cb = (wlan_serialization_cmd_callback) + tdls_del_peer_serialize_callback; + cmd.umac_cmd = req; + cmd.source = WLAN_UMAC_COMP_TDLS; + cmd.is_high_priority = false; + cmd.cmd_timeout_duration = TDLS_DEFAULT_SERIALIZE_CMD_TIMEOUT; + cmd.vdev = vdev; + cmd.is_blocking = true; + + ser_cmd_status = wlan_serialization_request(&cmd); + tdls_debug("req: 0x%pK wlan_serialization_request status:%d", req, + ser_cmd_status); + + switch (ser_cmd_status) { + case WLAN_SER_CMD_PENDING: + /* command moved to pending list. Do nothing */ + break; + case WLAN_SER_CMD_ACTIVE: + /* command moved to active list. Do nothing */ + break; + case WLAN_SER_CMD_DENIED_LIST_FULL: + case WLAN_SER_CMD_DENIED_RULES_FAILED: + case WLAN_SER_CMD_DENIED_UNSPECIFIED: + /* notify os interface about internal error*/ + status = tdls_internal_del_peer_rsp(req); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + /* cmd can't be serviced. + * release tdls_add_peer_request memory + */ + qdf_mem_free(req); + break; + default: + QDF_ASSERT(0); + status = QDF_STATUS_E_INVAL; + break; + } + + return status; +error: + status = tdls_internal_del_peer_rsp(req); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); +free_req: + qdf_mem_free(req); + return status; +} + +/** + * tdls_process_add_peer_rsp() - handle response for update TDLS peer + * @rsp: TDLS add peer response + * + * Return: QDF_STATUS_SUCCESS for success; other values if failed + */ +static QDF_STATUS tdls_update_peer_rsp(struct tdls_add_sta_rsp *rsp) +{ + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + struct tdls_soc_priv_obj *soc_obj; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct tdls_osif_indication ind; + + psoc = rsp->psoc; + if (!psoc) { + tdls_err("psoc is NULL"); + QDF_ASSERT(0); + return QDF_STATUS_E_FAILURE; + } + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, rsp->session_id, + WLAN_TDLS_SB_ID); + if (!vdev) { + tdls_err("invalid vdev: %d", rsp->session_id); + status = QDF_STATUS_E_INVAL; + goto error; + } + + tdls_release_serialization_command(vdev, WLAN_SER_CMD_TDLS_ADD_PEER); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); +error: + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (soc_obj && soc_obj->tdls_event_cb) { + ind.status = rsp->status_code; + ind.vdev = vdev; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_ADD_PEER, &ind); + } + qdf_mem_free(rsp); + + return status; +} + +/** + * tdls_process_send_mgmt_rsp() - handle response for send mgmt + * @rsp: TDLS send mgmt response + * + * Return: QDF_STATUS_SUCCESS for success; other values if failed + */ +QDF_STATUS tdls_process_send_mgmt_rsp(struct tdls_send_mgmt_rsp *rsp) +{ + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + struct tdls_vdev_priv_obj *tdls_vdev; + struct tdls_soc_priv_obj *tdls_soc = NULL; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct tdls_osif_indication ind; + + psoc = rsp->psoc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, rsp->session_id, + WLAN_TDLS_SB_ID); + if (!vdev) { + tdls_err("invalid vdev"); + status = QDF_STATUS_E_INVAL; + qdf_mem_free(rsp); + return status; + } + tdls_soc = wlan_psoc_get_tdls_soc_obj(psoc); + tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev); + if (!tdls_soc || !tdls_vdev) { + tdls_err("soc object:%pK, vdev object:%pK", tdls_soc, tdls_vdev); + status = QDF_STATUS_E_FAILURE; + } + + tdls_release_serialization_command(vdev, WLAN_SER_CMD_TDLS_SEND_MGMT); + + if (legacy_result_success == rsp->status_code) + goto free_rsp; + tdls_err("send mgmt failed. status code(=%d)", rsp->status_code); + status = QDF_STATUS_E_FAILURE; + + if (tdls_soc && tdls_soc->tdls_event_cb) { + ind.vdev = vdev; + ind.status = status; + tdls_soc->tdls_event_cb(tdls_soc->tdls_evt_cb_data, + TDLS_EVENT_MGMT_TX_ACK_CNF, &ind); + } + +free_rsp: + qdf_mem_free(rsp); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); + return status; +} + +/** + * tdls_send_mgmt_tx_completion() - process tx completion + * @tx_complete: TDLS mgmt completion info + * + * Return: QDF_STATUS_SUCCESS for success; other values if failed + */ +QDF_STATUS tdls_send_mgmt_tx_completion( + struct tdls_mgmt_tx_completion_ind *tx_complete) +{ + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + struct tdls_vdev_priv_obj *tdls_vdev; + struct tdls_soc_priv_obj *tdls_soc = NULL; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct tdls_osif_indication ind; + + psoc = tx_complete->psoc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + tx_complete->session_id, + WLAN_TDLS_SB_ID); + + if (!vdev) { + tdls_err("invalid vdev"); + status = QDF_STATUS_E_INVAL; + goto free_tx_complete; + } + + tdls_soc = wlan_psoc_get_tdls_soc_obj(psoc); + tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev); + + if (!tdls_soc || !tdls_vdev) { + tdls_err("soc object:%pK, vdev object:%pK", tdls_soc, tdls_vdev); + status = QDF_STATUS_E_FAILURE; + } + + if (tdls_soc && tdls_soc->tdls_event_cb) { + ind.vdev = vdev; + ind.status = tx_complete->tx_complete_status; + tdls_soc->tdls_event_cb(tdls_soc->tdls_evt_cb_data, + TDLS_EVENT_MGMT_TX_ACK_CNF, &ind); + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); +free_tx_complete: + qdf_mem_free(tx_complete); + return status; +} + +/** + * tdls_add_peer_rsp() - handle response for add TDLS peer + * @rsp: TDLS add peer response + * + * Return: QDF_STATUS_SUCCESS for success; other values if failed + */ +static QDF_STATUS tdls_add_peer_rsp(struct tdls_add_sta_rsp *rsp) +{ + uint8_t sta_idx; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + struct tdls_vdev_priv_obj *vdev_obj; + struct tdls_soc_priv_obj *soc_obj = NULL; + struct tdls_conn_info *conn_rec; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct tdls_osif_indication ind; + + psoc = rsp->psoc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, rsp->session_id, + WLAN_TDLS_SB_ID); + if (!vdev) { + tdls_err("invalid vdev: %d", rsp->session_id); + status = QDF_STATUS_E_INVAL; + goto error; + } + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + if (!soc_obj || !vdev_obj) { + tdls_err("soc object:%pK, vdev object:%pK", soc_obj, vdev_obj); + status = QDF_STATUS_E_FAILURE; + goto cmddone; + } + if (rsp->status_code) { + tdls_err("add sta failed. status code(=%d)", rsp->status_code); + status = QDF_STATUS_E_FAILURE; + } else { + conn_rec = soc_obj->tdls_conn_info; + for (sta_idx = 0; sta_idx < soc_obj->max_num_tdls_sta; + sta_idx++) { + if (INVALID_TDLS_PEER_ID == conn_rec[sta_idx].sta_id) { + conn_rec[sta_idx].session_id = rsp->session_id; + conn_rec[sta_idx].sta_id = rsp->sta_id; + qdf_copy_macaddr(&conn_rec[sta_idx].peer_mac, + &rsp->peermac); + tdls_warn("TDLS: STA IDX at %d is %d of mac " + QDF_MAC_ADDR_STR, sta_idx, + rsp->sta_id, QDF_MAC_ADDR_ARRAY + (rsp->peermac.bytes)); + break; + } + } + + if (sta_idx < soc_obj->max_num_tdls_sta) { + status = tdls_set_sta_id(vdev_obj, rsp->peermac.bytes, + rsp->sta_id); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("set staid failed"); + status = QDF_STATUS_E_FAILURE; + } + } else { + status = QDF_STATUS_E_FAILURE; + } + } + +cmddone: + tdls_release_serialization_command(vdev, WLAN_SER_CMD_TDLS_ADD_PEER); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); +error: + if (soc_obj && soc_obj->tdls_event_cb) { + ind.vdev = vdev; + ind.status = rsp->status_code; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_ADD_PEER, &ind); + } + qdf_mem_free(rsp); + + return status; +} + +QDF_STATUS tdls_process_add_peer_rsp(struct tdls_add_sta_rsp *rsp) +{ + tdls_debug("peer oper %d", rsp->tdls_oper); + + if (rsp->tdls_oper == TDLS_OPER_ADD) + return tdls_add_peer_rsp(rsp); + else if (rsp->tdls_oper == TDLS_OPER_UPDATE) + return tdls_update_peer_rsp(rsp); + + return QDF_STATUS_E_INVAL; +} + +QDF_STATUS tdls_process_del_peer_rsp(struct tdls_del_sta_rsp *rsp) +{ + uint8_t sta_idx, id; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + struct tdls_vdev_priv_obj *vdev_obj; + struct tdls_soc_priv_obj *soc_obj = NULL; + struct tdls_conn_info *conn_rec; + struct tdls_peer *curr_peer = NULL; + const uint8_t *macaddr; + struct tdls_osif_indication ind; + + tdls_debug("del peer rsp: vdev %d peer " QDF_MAC_ADDR_STR, + rsp->session_id, QDF_MAC_ADDR_ARRAY(rsp->peermac.bytes)); + psoc = rsp->psoc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, rsp->session_id, + WLAN_TDLS_SB_ID); + if (!vdev) { + tdls_err("invalid vdev: %d", rsp->session_id); + status = QDF_STATUS_E_INVAL; + goto error; + } + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + if (!soc_obj || !vdev_obj) { + tdls_err("soc object:%pK, vdev object:%pK", soc_obj, vdev_obj); + status = QDF_STATUS_E_FAILURE; + goto cmddone; + } + + conn_rec = soc_obj->tdls_conn_info; + for (sta_idx = 0; sta_idx < soc_obj->max_num_tdls_sta; sta_idx++) { + if (conn_rec[sta_idx].session_id != rsp->session_id || + conn_rec[sta_idx].sta_id != rsp->sta_id) + continue; + + macaddr = rsp->peermac.bytes; + tdls_warn("TDLS: del STA IDX = %x", rsp->sta_id); + curr_peer = tdls_find_peer(vdev_obj, macaddr); + if (curr_peer) { + tdls_debug(QDF_MAC_ADDR_STR " status is %d", + QDF_MAC_ADDR_ARRAY(macaddr), + curr_peer->link_status); + + id = wlan_vdev_get_id(vdev); + + if (TDLS_IS_LINK_CONNECTED(curr_peer)) { + soc_obj->tdls_dereg_peer( + soc_obj->tdls_peer_context, + id, curr_peer->sta_id); + tdls_decrement_peer_count(soc_obj); + } else if (TDLS_LINK_CONNECTING == + curr_peer->link_status) { + soc_obj->tdls_dereg_peer( + soc_obj->tdls_peer_context, + id, curr_peer->sta_id); + } + } + tdls_reset_peer(vdev_obj, macaddr); + conn_rec[sta_idx].sta_id = INVALID_TDLS_PEER_ID; + conn_rec[sta_idx].session_id = 0xff; + qdf_mem_zero(&conn_rec[sta_idx].peer_mac, + QDF_MAC_ADDR_SIZE); + + status = QDF_STATUS_SUCCESS; + break; + } + macaddr = rsp->peermac.bytes; + if (!curr_peer) { + curr_peer = tdls_find_peer(vdev_obj, macaddr); + + if (curr_peer) + tdls_set_peer_link_status(curr_peer, TDLS_LINK_IDLE, + (curr_peer->link_status == + TDLS_LINK_TEARING) ? + TDLS_LINK_UNSPECIFIED : + TDLS_LINK_DROPPED_BY_REMOTE); + } + +cmddone: + tdls_release_serialization_command(vdev, WLAN_SER_CMD_TDLS_DEL_PEER); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); +error: + + if (soc_obj && soc_obj->tdls_event_cb) { + ind.vdev = vdev; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_DEL_PEER, &ind); + } + qdf_mem_free(rsp); + + return status; +} + +static QDF_STATUS +tdls_wma_update_peer_state(struct tdls_soc_priv_obj *soc_obj, + struct tdls_peer_update_state *peer_state) +{ + struct scheduler_msg msg = {0,}; + QDF_STATUS status; + + tdls_debug("update TDLS peer " QDF_MAC_ADDR_STR " vdev %d, state %d", + QDF_MAC_ADDR_ARRAY(peer_state->peer_macaddr), + peer_state->vdev_id, peer_state->peer_state); + msg.type = soc_obj->tdls_update_peer_state; + msg.reserved = 0; + msg.bodyptr = peer_state; + + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_WMA, + QDF_MODULE_ID_WMA, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("scheduler_post_msg failed"); + status = QDF_STATUS_E_FAILURE; + } + + return status; +} + +static QDF_STATUS +tdls_update_uapsd(struct wlan_objmgr_psoc *psoc, struct wlan_objmgr_vdev *vdev, + uint8_t sta_id, uint32_t srvc_int, uint32_t sus_int, + uint8_t dir, uint8_t psb, uint32_t delay_interval) +{ + uint8_t i; + static const uint8_t ac[AC_PRIORITY_NUM] = {UAPSD_AC_VO, UAPSD_AC_VI, + UAPSD_AC_BK, UAPSD_AC_BE}; + static const uint8_t tid[AC_PRIORITY_NUM] = {7, 5, 2, 3}; + uint32_t vdev_id; + + struct sta_uapsd_params tdls_uapsd_params; + struct sta_uapsd_trig_params tdls_trig_params; + struct wlan_objmgr_peer *bsspeer; + uint8_t macaddr[QDF_MAC_ADDR_SIZE]; + QDF_STATUS status; + + if (!psb) { + tdls_debug("No need to configure auto trigger:psb is 0"); + return QDF_STATUS_SUCCESS; + } + vdev_id = wlan_vdev_get_id(vdev); + bsspeer = wlan_vdev_get_bsspeer(vdev); + if (!bsspeer) { + tdls_err("bss peer is NULL"); + return QDF_STATUS_E_FAILURE; + } + wlan_vdev_obj_lock(vdev); + qdf_mem_copy(macaddr, + wlan_peer_get_macaddr(bsspeer), QDF_MAC_ADDR_SIZE); + wlan_vdev_obj_unlock(vdev); + + tdls_debug("TDLS uapsd id %d, srvc %d, sus %d, dir %d psb %d delay %d", + sta_id, srvc_int, sus_int, dir, psb, delay_interval); + for (i = 0; i < AC_PRIORITY_NUM; i++) { + tdls_uapsd_params.wmm_ac = ac[i]; + tdls_uapsd_params.user_priority = tid[i]; + tdls_uapsd_params.service_interval = srvc_int; + tdls_uapsd_params.delay_interval = delay_interval; + tdls_uapsd_params.suspend_interval = sus_int; + + tdls_trig_params.vdevid = vdev_id; + tdls_trig_params.num_ac = 1; + tdls_trig_params.auto_triggerparam = &tdls_uapsd_params; + + qdf_mem_copy(tdls_trig_params.peer_addr, + macaddr, QDF_MAC_ADDR_SIZE); + status = tgt_tdls_set_uapsd(psoc, &tdls_trig_params); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("Failed to set uapsd for vdev %d, status %d", + vdev_id, status); + } + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_process_enable_link(struct tdls_oper_request *req) +{ + struct tdls_peer *peer; + struct tdls_vdev_priv_obj *vdev_obj; + struct tdls_soc_priv_obj *soc_obj; + struct wlan_objmgr_vdev *vdev; + uint8_t *mac; + struct tdls_peer_update_state *peer_update_param; + QDF_STATUS status; + uint32_t feature; + uint8_t id; + + vdev = req->vdev; + if (!vdev) { + tdls_err("NULL vdev object"); + qdf_mem_free(req); + return QDF_STATUS_E_NULL_VALUE; + } + + /* vdev reference cnt is acquired in ucfg_tdls_oper */ + vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + + if (!vdev_obj || !soc_obj) { + tdls_err("tdls vdev_obj: %pK soc_obj: %pK", vdev_obj, soc_obj); + status = QDF_STATUS_E_NULL_VALUE; + goto error; + } + + mac = req->peer_addr; + peer = tdls_find_peer(vdev_obj, mac); + if (!peer) { + tdls_err(QDF_MAC_ADDR_STR + " not found, ignore NL80211_TDLS_ENABLE_LINK", + QDF_MAC_ADDR_ARRAY(mac)); + status = QDF_STATUS_E_INVAL; + goto error; + } + + tdls_debug("enable link for peer " QDF_MAC_ADDR_STR " link state %d", + QDF_MAC_ADDR_ARRAY(mac), peer->link_status); + if (!TDLS_STA_INDEX_CHECK(peer->sta_id)) { + tdls_err("invalid sta idx %u for " QDF_MAC_ADDR_STR, + peer->sta_id, QDF_MAC_ADDR_ARRAY(mac)); + status = QDF_STATUS_E_INVAL; + goto error; + } + + peer->tdls_support = TDLS_CAP_SUPPORTED; + if (TDLS_LINK_CONNECTED != peer->link_status) + tdls_set_peer_link_status(peer, TDLS_LINK_CONNECTED, + TDLS_LINK_SUCCESS); + + id = wlan_vdev_get_id(vdev); + status = soc_obj->tdls_reg_peer(soc_obj->tdls_peer_context, + id, mac, peer->sta_id, + peer->qos); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("TDLS register peer fail, status %d", status); + goto error; + } + + peer_update_param = qdf_mem_malloc(sizeof(*peer_update_param)); + if (!peer_update_param) { + tdls_err("memory allocation failed"); + status = QDF_STATUS_E_NOMEM; + goto error; + } + + tdls_extract_peer_state_param(peer_update_param, peer); + + status = tdls_wma_update_peer_state(soc_obj, peer_update_param); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(peer_update_param); + status = QDF_STATUS_E_PERM; + goto error; + } + + tdls_increment_peer_count(soc_obj); + feature = soc_obj->tdls_configs.tdls_feature_flags; + + if (soc_obj->tdls_dp_vdev_update) + soc_obj->tdls_dp_vdev_update(&soc_obj->soc, + peer->sta_id, + soc_obj->tdls_update_dp_vdev_flags, + ((peer->link_status == + TDLS_LINK_CONNECTED) ? true : false)); + + tdls_debug("TDLS buffer sta: %d, uapsd_mask %d", + TDLS_IS_BUFFER_STA_ENABLED(feature), + soc_obj->tdls_configs.tdls_uapsd_mask); + + if (TDLS_IS_BUFFER_STA_ENABLED(feature) || + soc_obj->tdls_configs.tdls_uapsd_mask) + tdls_update_uapsd(soc_obj->soc, + vdev, peer->sta_id, 0, 0, BI_DIR, 1, + soc_obj->tdls_configs.delayed_trig_framint); +error: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(req); + + return status; +} + +/** + * tdls_config_force_peer() - configure an externally controllable TDLS peer + * @req: TDLS operation request + * + * This is not the tdls_process_cmd function. No need to acquire the reference + * count, release reference count and free the request, the caller handle it + * correctly. + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +static QDF_STATUS tdls_config_force_peer( + struct tdls_oper_config_force_peer_request *req) +{ + struct tdls_peer *peer; + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + struct wlan_objmgr_pdev *pdev; + struct wlan_objmgr_vdev *vdev; + const uint8_t *macaddr; + uint32_t feature; + QDF_STATUS status; + struct tdls_peer_update_state *peer_update_param; + + macaddr = req->peer_addr; + tdls_debug("NL80211_TDLS_SETUP for " QDF_MAC_ADDR_STR, + QDF_MAC_ADDR_ARRAY(macaddr)); + + vdev = req->vdev; + pdev = wlan_vdev_get_pdev(vdev); + vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (!pdev || !vdev_obj || !soc_obj) { + tdls_err("pdev: %pK, vdev_obj: %pK, soc_obj: %pK", + pdev, vdev_obj, soc_obj); + return QDF_STATUS_E_INVAL; + } + + feature = soc_obj->tdls_configs.tdls_feature_flags; + if (!TDLS_IS_EXTERNAL_CONTROL_ENABLED(feature) || + !TDLS_IS_IMPLICIT_TRIG_ENABLED(feature)) { + tdls_err("TDLS ext ctrl or Imp Trig not enabled, %x", feature); + return QDF_STATUS_E_NOSUPPORT; + } + + peer_update_param = qdf_mem_malloc(sizeof(*peer_update_param)); + if (!peer_update_param) { + tdls_err("memory allocation failed"); + return QDF_STATUS_E_NOMEM; + } + + peer = tdls_get_peer(vdev_obj, macaddr); + if (!peer) { + tdls_err("peer " QDF_MAC_ADDR_STR " does not exist", + QDF_MAC_ADDR_ARRAY(macaddr)); + status = QDF_STATUS_E_NULL_VALUE; + goto error; + } + status = tdls_set_force_peer(vdev_obj, macaddr, true); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("set force peer failed"); + goto error; + } + + /* Update the peer mac to firmware, so firmware could update the + * connection table + */ + peer_update_param->vdev_id = wlan_vdev_get_id(vdev); + qdf_mem_copy(peer_update_param->peer_macaddr, + macaddr, QDF_MAC_ADDR_SIZE); + peer_update_param->peer_state = TDLS_PEER_ADD_MAC_ADDR; + + status = tdls_wma_update_peer_state(soc_obj, peer_update_param); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("update peer state failed"); + goto error; + } + + soc_obj->tdls_external_peer_count++; + + /* Validate if off channel is DFS channel */ + if (wlan_reg_is_dfs_ch(pdev, req->chan)) { + tdls_err("Resetting TDLS off-channel from %d to %d", + req->chan, WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_DEF); + req->chan = WLAN_TDLS_PREFERRED_OFF_CHANNEL_NUM_DEF; + } + tdls_set_extctrl_param(peer, req->chan, req->max_latency, req->op_class, + req->min_bandwidth); + + tdls_set_callback(peer, req->callback); + + tdls_set_ct_mode(soc_obj->soc); + if (soc_obj->enable_tdls_connection_tracker) + tdls_implicit_enable(vdev_obj); + + return status; +error: + qdf_mem_free(peer_update_param); + return status; +} + +/** + * tdls_process_setup_peer() - process configure an externally + * controllable TDLS peer + * @req: TDLS operation request + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +QDF_STATUS tdls_process_setup_peer(struct tdls_oper_request *req) +{ + struct tdls_oper_config_force_peer_request peer_req; + struct tdls_soc_priv_obj *soc_obj; + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status; + + tdls_debug("Configure external TDLS peer " QDF_MAC_ADDR_STR, + QDF_MAC_ADDR_ARRAY(req->peer_addr)); + + /* reference cnt is acquired in ucfg_tdls_oper */ + vdev = req->vdev; + if (!vdev) { + tdls_err("NULL vdev object"); + status = QDF_STATUS_E_NULL_VALUE; + goto freereq; + } + + qdf_mem_zero(&peer_req, sizeof(peer_req)); + peer_req.vdev = vdev; + qdf_mem_copy(peer_req.peer_addr, req->peer_addr, QDF_MAC_ADDR_SIZE); + + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (!soc_obj) { + tdls_err("NULL soc object"); + status = QDF_STATUS_E_INVAL; + goto error; + } + + peer_req.chan = soc_obj->tdls_configs.tdls_pre_off_chan_num; + + status = tdls_config_force_peer(&peer_req); +error: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); +freereq: + qdf_mem_free(req); + + return status; +} + +QDF_STATUS tdls_process_remove_force_peer(struct tdls_oper_request *req) +{ + struct tdls_peer *peer; + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + struct wlan_objmgr_vdev *vdev; + const uint8_t *macaddr; + uint32_t feature; + QDF_STATUS status; + struct tdls_peer_update_state *peer_update_param; + struct tdls_osif_indication ind; + + macaddr = req->peer_addr; + tdls_debug("NL80211_TDLS_TEARDOWN for " QDF_MAC_ADDR_STR, + QDF_MAC_ADDR_ARRAY(macaddr)); + + vdev = req->vdev; + if (!vdev) { + tdls_err("NULL vdev object"); + qdf_mem_free(req); + return QDF_STATUS_E_NULL_VALUE; + } + + /* reference cnt is acquired in ucfg_tdls_oper */ + vdev_obj = wlan_vdev_get_tdls_vdev_obj(req->vdev); + soc_obj = wlan_vdev_get_tdls_soc_obj(req->vdev); + if (!soc_obj || !vdev_obj) { + tdls_err("soc_obj: %pK, vdev_obj: %pK", soc_obj, vdev_obj); + status = QDF_STATUS_E_INVAL; + goto error; + } + + feature = soc_obj->tdls_configs.tdls_feature_flags; + if (!TDLS_IS_EXTERNAL_CONTROL_ENABLED(feature) || + !TDLS_IS_IMPLICIT_TRIG_ENABLED(feature)) { + tdls_err("TDLS ext ctrl or Imp Trig not enabled, %x", feature); + status = QDF_STATUS_E_NOSUPPORT; + goto error; + } + + peer = tdls_find_peer(vdev_obj, macaddr); + if (!peer) { + tdls_err("peer matching " QDF_MAC_ADDR_STR " not found", + QDF_MAC_ADDR_ARRAY(macaddr)); + status = QDF_STATUS_E_NULL_VALUE; + goto error; + } + + tdls_set_peer_link_status(peer, TDLS_LINK_TEARING, + TDLS_LINK_UNSPECIFIED); + + if (soc_obj->tdls_dp_vdev_update) + soc_obj->tdls_dp_vdev_update(&soc_obj->soc, + peer->sta_id, + soc_obj->tdls_update_dp_vdev_flags, + false); + + if (soc_obj->tdls_event_cb) { + qdf_mem_copy(ind.peer_mac, macaddr, QDF_MAC_ADDR_SIZE); + ind.vdev = vdev; + ind.reason = TDLS_TEARDOWN_PEER_UNSPEC_REASON; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_TEARDOWN_REQ, &ind); + } + + status = tdls_set_force_peer(vdev_obj, macaddr, false); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("set force peer failed"); + status = QDF_STATUS_E_INVAL; + goto error; + } + + if (soc_obj->tdls_external_peer_count) + soc_obj->tdls_external_peer_count--; + + tdls_set_callback(peer, NULL); + peer_update_param = qdf_mem_malloc(sizeof(*peer_update_param)); + if (!peer_update_param) { + tdls_err("memory allocation failed"); + status = QDF_STATUS_E_NOMEM; + goto error; + } + + peer_update_param->vdev_id = wlan_vdev_get_id(vdev); + qdf_mem_copy(peer_update_param->peer_macaddr, + macaddr, QDF_MAC_ADDR_SIZE); + peer_update_param->peer_state = TDLS_PEER_REMOVE_MAC_ADDR; + status = tdls_wma_update_peer_state(soc_obj, peer_update_param); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(peer_update_param); + goto error; + } + tdls_set_ct_mode(soc_obj->soc); + if (!soc_obj->enable_tdls_connection_tracker) + tdls_implicit_disable(vdev_obj); + +error: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(req); + + return status; +} + +static const char *tdls_evt_to_str(enum tdls_event_msg_type type) +{ + switch (type) { + case TDLS_SHOULD_DISCOVER: + return "SHOULD_DISCOVER"; + case TDLS_SHOULD_TEARDOWN: + return "SHOULD_TEARDOWN"; + case TDLS_PEER_DISCONNECTED: + return "SHOULD_PEER_DISCONNECTED"; + case TDLS_CONNECTION_TRACKER_NOTIFY: + return "CONNECTION_TRACKER_NOTIFICATION"; + default: + return "INVALID_TYPE"; + } +} + +QDF_STATUS tdls_process_should_discover(struct wlan_objmgr_vdev *vdev, + struct tdls_event_info *evt) +{ + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + struct tdls_peer *curr_peer; + uint32_t feature; + uint16_t type; + + /*TODO ignore this if any concurrency detected*/ + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + type = evt->message_type; + + tdls_debug("TDLS %s: " QDF_MAC_ADDR_STR "reason %d", + tdls_evt_to_str(type), + QDF_MAC_ADDR_ARRAY(evt->peermac.bytes), + evt->peer_reason); + if (!soc_obj || !vdev_obj) { + tdls_err("soc_obj: %pK, vdev_obj: %pK, ignore %s", + soc_obj, vdev_obj, tdls_evt_to_str(type)); + return QDF_STATUS_E_NULL_VALUE; + } + if (soc_obj->tdls_nss_switch_in_progress) { + tdls_err("TDLS antenna switching, ignore %s", + tdls_evt_to_str(type)); + return QDF_STATUS_SUCCESS; + } + + curr_peer = tdls_get_peer(vdev_obj, evt->peermac.bytes); + if (!curr_peer) { + tdls_notice("curr_peer is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + if (TDLS_LINK_CONNECTED == curr_peer->link_status) { + tdls_err("TDLS link status is connected, ignore"); + return QDF_STATUS_SUCCESS; + } + + feature = soc_obj->tdls_configs.tdls_feature_flags; + if (TDLS_IS_EXTERNAL_CONTROL_ENABLED(feature) && + !curr_peer->is_forced_peer) { + tdls_debug("curr_peer is not forced, ignore %s", + tdls_evt_to_str(type)); + return QDF_STATUS_SUCCESS; + } + + tdls_debug("initiate TDLS setup on %s, ext: %d, force: %d, reason: %d", + tdls_evt_to_str(type), + TDLS_IS_EXTERNAL_CONTROL_ENABLED(feature), + curr_peer->is_forced_peer, evt->peer_reason); + vdev_obj->curr_candidate = curr_peer; + tdls_implicit_send_discovery_request(vdev_obj); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_process_should_teardown(struct wlan_objmgr_vdev *vdev, + struct tdls_event_info *evt) +{ + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + struct tdls_peer *curr_peer; + uint32_t reason; + uint16_t type; + + type = evt->message_type; + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + + tdls_debug("TDLS %s: " QDF_MAC_ADDR_STR "reason %d", + tdls_evt_to_str(type), + QDF_MAC_ADDR_ARRAY(evt->peermac.bytes), evt->peer_reason); + + if (!soc_obj || !vdev_obj) { + tdls_err("soc_obj: %pK, vdev_obj: %pK, ignore %s", + soc_obj, vdev_obj, tdls_evt_to_str(type)); + return QDF_STATUS_E_NULL_VALUE; + } + + curr_peer = tdls_find_peer(vdev_obj, evt->peermac.bytes); + if (!curr_peer) { + tdls_notice("curr_peer is null"); + return QDF_STATUS_E_NULL_VALUE; + } + + reason = evt->peer_reason; + if (TDLS_LINK_CONNECTED == curr_peer->link_status) { + tdls_err("%s reason: %d for" QDF_MAC_ADDR_STR, + tdls_evt_to_str(type), evt->peer_reason, + QDF_MAC_ADDR_ARRAY(evt->peermac.bytes)); + if (reason == TDLS_TEARDOWN_RSSI || + reason == TDLS_DISCONNECTED_PEER_DELETE || + reason == TDLS_TEARDOWN_PTR_TIMEOUT || + reason == TDLS_TEARDOWN_NO_RSP) + reason = TDLS_TEARDOWN_PEER_UNREACHABLE; + else + reason = TDLS_TEARDOWN_PEER_UNSPEC_REASON; + + tdls_indicate_teardown(vdev_obj, curr_peer, reason); + } else { + tdls_err("TDLS link is not connected, ignore %s", + tdls_evt_to_str(type)); + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_process_connection_tracker_notify(struct wlan_objmgr_vdev *vdev, + struct tdls_event_info *evt) +{ + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + uint16_t type; + + type = evt->message_type; + soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + + if (!soc_obj || !vdev_obj) { + tdls_err("soc_obj: %pK, vdev_obj: %pK, ignore %s", + soc_obj, vdev_obj, tdls_evt_to_str(type)); + return QDF_STATUS_E_NULL_VALUE; + } + + /*TODO connection tracker update*/ + return QDF_STATUS_SUCCESS; +} + +/** + * tdls_process_set_responder() - Set/clear TDLS peer's responder role + * @set_req: set responder request + * + * Return: 0 for success or -EINVAL otherwise + */ +static +int tdls_process_set_responder(struct tdls_set_responder_req *set_req) +{ + struct tdls_peer *curr_peer; + struct tdls_vdev_priv_obj *tdls_vdev; + + tdls_vdev = wlan_vdev_get_tdls_vdev_obj(set_req->vdev); + if (!tdls_vdev) { + tdls_err("tdls vdev obj is NULL"); + return -EINVAL; + } + curr_peer = tdls_get_peer(tdls_vdev, set_req->peer_mac); + if (curr_peer == NULL) { + tdls_err("curr_peer is NULL"); + return -EINVAL; + } + + curr_peer->is_responder = set_req->responder; + return 0; +} + + +/** + * tdls_set_responder() - Set/clear TDLS peer's responder role + * @set_req: set responder request + * + * Return: 0 for success or -EINVAL otherwise + */ +int tdls_set_responder(struct tdls_set_responder_req *set_req) +{ + QDF_STATUS status; + + if (!set_req || !set_req->vdev) { + tdls_err("Invalid input params %pK", set_req); + return -EINVAL; + } + + status = wlan_objmgr_vdev_try_get_ref(set_req->vdev, WLAN_TDLS_NB_ID); + if (QDF_STATUS_SUCCESS != status) { + tdls_err("vdev object is deleted"); + return -EINVAL; + } + + status = tdls_process_set_responder(set_req); + + wlan_objmgr_vdev_release_ref(set_req->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(set_req); + return status; +} + +static int tdls_teardown_links(struct tdls_soc_priv_obj *soc_obj, uint32_t mode) +{ + uint8_t staidx; + struct tdls_peer *curr_peer; + struct tdls_conn_info *conn_rec; + int ret = 0; + + conn_rec = soc_obj->tdls_conn_info; + for (staidx = 0; staidx < soc_obj->max_num_tdls_sta; staidx++) { + if (conn_rec[staidx].sta_id == INVALID_TDLS_PEER_ID) + continue; + + curr_peer = tdls_find_all_peer(soc_obj, + conn_rec[staidx].peer_mac.bytes); + if (!curr_peer) + continue; + + /* if supported only 1x1, skip it */ + if (curr_peer->spatial_streams == HW_MODE_SS_1x1) + continue; + + tdls_debug("Indicate TDLS teardown (staId %d)", + curr_peer->sta_id); + tdls_indicate_teardown(curr_peer->vdev_priv, curr_peer, + TDLS_TEARDOWN_PEER_UNSPEC_REASON); + + soc_obj->tdls_teardown_peers_cnt++; + } + + if (soc_obj->tdls_teardown_peers_cnt >= 1) { + soc_obj->tdls_nss_switch_in_progress = true; + tdls_debug("TDLS peers to be torn down = %d", + soc_obj->tdls_teardown_peers_cnt); + + /* set the antenna switch transition mode */ + if (mode == HW_MODE_SS_1x1) { + soc_obj->tdls_nss_transition_mode = + TDLS_NSS_TRANSITION_S_2x2_to_1x1; + ret = -EAGAIN; + } else { + soc_obj->tdls_nss_transition_mode = + TDLS_NSS_TRANSITION_S_1x1_to_2x2; + ret = 0; + } + tdls_debug("TDLS teardown for antenna switch operation starts"); + } + + return ret; +} + +QDF_STATUS tdls_process_antenna_switch(struct tdls_antenna_switch_request *req) +{ + QDF_STATUS status; + struct tdls_soc_priv_obj *soc_obj; + struct tdls_vdev_priv_obj *vdev_obj; + struct wlan_objmgr_vdev *vdev = NULL; + uint32_t vdev_nss; + int ant_switch_state = 0; + uint32_t vdev_id; + enum QDF_OPMODE opmode; + uint8_t channel; + struct tdls_osif_indication ind; + + if (!req) { + tdls_err("null req"); + return QDF_STATUS_E_INVAL; + } + + vdev = req->vdev; + if (!vdev) { + tdls_err("null vdev"); + qdf_mem_free(req); + return QDF_STATUS_E_INVAL; + } + + status = tdls_get_vdev_objects(vdev, &vdev_obj, &soc_obj); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev_obj & soc_obj"); + goto get_obj_err; + } + + if (soc_obj->connected_peer_count == 0) + goto ant_sw_done; + + if (soc_obj->tdls_nss_switch_in_progress) { + if (!soc_obj->tdls_nss_teardown_complete) { + tdls_err("TDLS antenna switch is in progress"); + goto ant_sw_in_progress; + } else { + goto ant_sw_done; + } + } + + vdev_id = wlan_vdev_get_id(vdev); + opmode = wlan_vdev_mlme_get_opmode(vdev); + channel = policy_mgr_get_channel(soc_obj->soc, + policy_mgr_convert_device_mode_to_qdf_type(opmode), + &vdev_id); + + /* Check supported nss for TDLS, if is 1x1, no need to teardown links */ + if (WLAN_REG_IS_24GHZ_CH(channel)) + vdev_nss = soc_obj->tdls_configs.tdls_vdev_nss_2g; + else + vdev_nss = soc_obj->tdls_configs.tdls_vdev_nss_5g; + + if (vdev_nss == HW_MODE_SS_1x1) { + tdls_debug("Supported NSS is 1x1, no need to teardown TDLS links"); + goto ant_sw_done; + } + + if (tdls_teardown_links(soc_obj, req->mode) == 0) + goto ant_sw_done; + +ant_sw_in_progress: + ant_switch_state = -EAGAIN; +ant_sw_done: + if (soc_obj->tdls_event_cb) { + ind.vdev = vdev; + ind.status = ant_switch_state; + soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data, + TDLS_EVENT_ANTENNA_SWITCH, &ind); + } + + if (soc_obj->tdls_nss_switch_in_progress && + soc_obj->tdls_nss_teardown_complete) { + soc_obj->tdls_nss_switch_in_progress = false; + soc_obj->tdls_nss_teardown_complete = false; + } + tdls_debug("tdls_nss_switch_in_progress: %d tdls_nss_teardown_complete: %d", + soc_obj->tdls_nss_switch_in_progress, + soc_obj->tdls_nss_teardown_complete); + +get_obj_err: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(req); + + return status; +} + +QDF_STATUS tdls_antenna_switch_flush_callback(struct scheduler_msg *msg) +{ + struct tdls_antenna_switch_request *req; + + if (!msg || !msg->bodyptr) { + tdls_err("msg: 0x%pK", msg); + return QDF_STATUS_E_NULL_VALUE; + } + req = msg->bodyptr; + wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(req); + + return QDF_STATUS_SUCCESS; +} + +void wlan_tdls_offchan_parms_callback(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + tdls_err("vdev is NULL"); + return; + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); +} + +int tdls_process_set_offchannel(struct tdls_set_offchannel *req) +{ + int status; + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct tdls_soc_priv_obj *tdls_soc_obj; + + if (tdls_get_vdev_objects(req->vdev, &tdls_vdev_obj, &tdls_soc_obj) != + QDF_STATUS_SUCCESS) { + status = -ENOTSUPP; + goto free; + } + + tdls_debug("TDLS offchannel to be configured %d", req->offchannel); + + if (req->offchannel) + status = tdls_set_tdls_offchannel(tdls_soc_obj, + req->offchannel); + else + status = -ENOTSUPP; + +free: + + if (req->callback) + req->callback(req->vdev); + qdf_mem_free(req); + + return status; +} + +int tdls_process_set_offchan_mode(struct tdls_set_offchanmode *req) +{ + int status; + + tdls_debug("TDLS offchan mode to be configured %d", req->offchan_mode); + status = tdls_set_tdls_offchannelmode(req->vdev, req->offchan_mode); + + if (req->callback) + req->callback(req->vdev); + qdf_mem_free(req); + + return status; +} + +int tdls_process_set_secoffchanneloffset( + struct tdls_set_secoffchanneloffset *req) +{ + int status; + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct tdls_soc_priv_obj *tdls_soc_obj; + + if (tdls_get_vdev_objects(req->vdev, &tdls_vdev_obj, &tdls_soc_obj) != + QDF_STATUS_SUCCESS) { + status = -ENOTSUPP; + goto free; + } + + tdls_debug("TDLS offchannel offset to be configured %d", + req->offchan_offset); + status = tdls_set_tdls_secoffchanneloffset(tdls_soc_obj, + req->offchan_offset); + +free: + + if (req->callback) + req->callback(req->vdev); + qdf_mem_free(req); + + return status; +} diff --git a/components/tdls/core/src/wlan_tdls_cmds_process.h b/components/tdls/core/src/wlan_tdls_cmds_process.h new file mode 100644 index 000000000000..e66b41aa69e5 --- /dev/null +++ b/components/tdls/core/src/wlan_tdls_cmds_process.h @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_cmds_process.h + * + * TDLS north bound commands include file + */ + +#ifndef _WLAN_TDLS_CMDS_PROCESS_H_ +#define _WLAN_TDLS_CMDS_PROCESS_H_ + +#define TDLS_IS_SETUP_ACTION(action) \ + ((TDLS_SETUP_REQUEST <= action) && \ + (TDLS_SETUP_CONFIRM >= action)) + +/** + * enum legacy_result_code - defined to comply with tSirResultCodes, need refine + * when mlme converged. + * @legacy_result_success: success + * @legacy_result_max: max result value + */ +enum legacy_result_code { + legacy_result_success, + legacy_result_max = 0x7FFFFFFF +}; + +/** + * struct tdls_send_mgmt_rsp - TDLS Response struct PE --> TDLS module + * same as struct tSirSmeRsp + * @message_type: message type eWNI_SME_TDLS_SEND_MGMT_RSP + * @length: message length + * @session_id: session id + * @transaction_id: transaction id + * @status_code: status code as tSirResultCodes + * @psoc: soc object + */ +struct tdls_send_mgmt_rsp { + uint16_t message_type; + uint16_t length; + uint8_t session_id; + uint16_t transaction_id; + enum legacy_result_code status_code; + struct wlan_objmgr_psoc *psoc; +}; + +/** + * struct tdls_mgmt_tx_completion_ind - TDLS TX completion PE --> TDLS module + * same as struct sSirMgmtTxCompletionInd + * @message_type: message type eWNI_SME_MGMT_FRM_TX_COMPLETION_IND + * @length: message length + * @session_id: session id + * @tx_complete_status: tx complete status + * @psoc: soc object + */ +struct tdls_mgmt_tx_completion_ind { + uint16_t message_type; + uint16_t length; + uint8_t session_id; /* Session ID */ + uint32_t tx_complete_status; + struct wlan_objmgr_psoc *psoc; +}; + +/** + * 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 + * @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; + 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_enable_link() - enable TDLS link + * @req: TDLS enable link request + * + * Return: QDF_STATUS_SUCCESS if success; other value if failed + */ +QDF_STATUS tdls_process_enable_link(struct tdls_oper_request *req); + +/** + * tdls_process_setup_peer() - process configure an externally + * controllable TDLS peer + * @req: TDLS configure force peer request + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +QDF_STATUS tdls_process_setup_peer(struct tdls_oper_request *req); + +/** + * tdls_process_remove_force_peer() - process remove an externally controllable + * TDLS peer + * @req: TDLS operation request + * + * Return: QDF_STATUS_SUCCESS if success; other values if failed + */ +QDF_STATUS tdls_process_remove_force_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_process_antenna_switch() - handle TDLS antenna switch + * @req: TDLS antenna switch request + * + * Rely on callback to indicate the antenna switch state to caller. + * + * Return: QDF_STATUS_SUCCESS if success; other value if failed. + */ +QDF_STATUS tdls_process_antenna_switch(struct tdls_antenna_switch_request *req); + +/** + * tdls_antenna_switch_flush_callback() - flush TDLS antenna switch request + * @msg: scheduler message contains tdls antenna switch event + * + * This function call is invoked when scheduler thread is going down + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_antenna_switch_flush_callback(struct scheduler_msg *msg); + +/** + * 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_reset_nss() - reset tdls nss parameters + * @tdls_soc: TDLS soc object + * @action_code: action code + * + * Return: None + */ +void tdls_reset_nss(struct tdls_soc_priv_obj *tdls_soc, + uint8_t action_code); + +/** + * tdls_release_serialization_command() - TDLS wrapper to + * relases serialization command. + * @vdev: Object manager vdev + * @type: command to release. + * + * Return: None + */ + +void +tdls_release_serialization_command(struct wlan_objmgr_vdev *vdev, + enum wlan_serialization_cmd_type type); + +/** + * tdls_set_cap() - set TDLS capability type + * @tdls_vdev: tdls vdev object + * @mac: peer mac address + * @cap: TDLS capability type + * + * Return: 0 if successful or negative errno otherwise + */ +int tdls_set_cap(struct tdls_vdev_priv_obj *tdls_vdev, const uint8_t *mac, + enum tdls_peer_capab cap); + +/** + * tdls_process_send_mgmt_rsp() - handle response for send mgmt + * @rsp: TDLS send mgmt response + * + * Return: QDF_STATUS_SUCCESS for success; other values if failed + */ +QDF_STATUS tdls_process_send_mgmt_rsp(struct tdls_send_mgmt_rsp *rsp); + +/** + * tdls_send_mgmt_tx_completion() - process tx completion + * @tx_complete: TDLS mgmt completion info + * + * Return: QDF_STATUS_SUCCESS for success; other values if failed + */ +QDF_STATUS tdls_send_mgmt_tx_completion( + struct tdls_mgmt_tx_completion_ind *tx_complete); + +/** + * 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); + +/** + * tdls_process_should_discover() - handle tdls should_discover event + * @vdev: vdev object + * @evt: event info + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_process_should_discover(struct wlan_objmgr_vdev *vdev, + struct tdls_event_info *evt); + +/** + * tdls_process_should_teardown() - handle tdls should_teardown event + * @vdev: vdev object + * @evt: event info + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_process_should_teardown(struct wlan_objmgr_vdev *vdev, + struct tdls_event_info *evt); + +/** + * tdls_process_connection_tracker_notify() -handle tdls connect tracker notify + * @vdev: vdev object + * @evt: event info + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_process_connection_tracker_notify(struct wlan_objmgr_vdev *vdev, + struct tdls_event_info *evt); + +/** + * tdls_validate_mgmt_request() -validate mgmt request + * @tdls_validate: action frame request + * + * Return: 0 for success or -EINVAL otherwise + */ +int tdls_validate_mgmt_request(struct tdls_action_frame_request *tdls_mgmt_req); + +/** + * tdls_set_responder() - Set/clear TDLS peer's responder role + * @set_req: set responder request + * + * Return: 0 for success or -EINVAL otherwise + */ +int tdls_set_responder(struct tdls_set_responder_req *set_req); + +/** + * tdls_decrement_peer_count() - decrement connected TDLS peer counter + * @soc_obj: TDLS soc object + * + * Used in scheduler thread context, no lock needed. + * + * Return: None. + */ +void tdls_decrement_peer_count(struct tdls_soc_priv_obj *soc_obj); + +/** + * wlan_tdls_offchan_parms_callback() - Callback to release ref count + * @vdev: vdev object + * + * Return: none + */ +void wlan_tdls_offchan_parms_callback(struct wlan_objmgr_vdev *vdev); + +/** + * tdls_process_set_offchannel() - Handle set offchannel request for TDLS + * @req: TDLS set offchannel request + * + * Return: int status + */ +int tdls_process_set_offchannel(struct tdls_set_offchannel *req); + +/** + * tdls_process_set_offchan_mode() - Handle set offchan mode request for TDLS + * @req: TDLS set offchannel mode request + * + * Return: int status + */ +int tdls_process_set_offchan_mode(struct tdls_set_offchanmode *req); + +/** + * tdls_process_set_secoffchanneloffset() - Handle set sec offchannel + * offset request for TDLS + * @req: TDLS set secoffchannel offchannel request + * + * Return: int status + */ +int tdls_process_set_secoffchanneloffset( + struct tdls_set_secoffchanneloffset *req); + +#endif diff --git a/components/tdls/core/src/wlan_tdls_ct.c b/components/tdls/core/src/wlan_tdls_ct.c new file mode 100644 index 000000000000..f62a5e1fd929 --- /dev/null +++ b/components/tdls/core/src/wlan_tdls_ct.c @@ -0,0 +1,1319 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_ct.c + * + * TDLS connection tracker function definitions + */ + +#include "wlan_tdls_main.h" +#include "wlan_tdls_peer.h" +#include "wlan_tdls_ct.h" +#include "wlan_tdls_cmds_process.h" + +bool tdls_is_vdev_connected(struct wlan_objmgr_vdev *vdev) +{ + struct wlan_objmgr_peer *peer; + enum wlan_peer_state peer_state; + + peer = wlan_vdev_get_bsspeer(vdev); + + if (!peer) { + tdls_err("peer is null"); + return false; + } + + peer_state = wlan_peer_mlme_get_state(peer); + + if (peer_state != WLAN_ASSOC_STATE) { + tdls_err("peer state: %d", peer_state); + return false; + } + + return true; +} + +bool tdls_is_vdev_authenticated(struct wlan_objmgr_vdev *vdev) +{ + struct wlan_objmgr_peer *peer; + bool is_authenticated = false; + + peer = wlan_vdev_get_bsspeer(vdev); + + if (!peer) { + tdls_err("peer is null"); + return false; + } + + is_authenticated = wlan_peer_mlme_get_auth_state(peer); + + return is_authenticated; +} + +/** + * tdls_peer_reset_discovery_processed() - reset discovery status + * @tdls_vdev: TDLS vdev object + * + * This function resets discovery processing bit for all TDLS peers + * + * Caller has to take the lock before calling this function + * + * Return: 0 + */ +static int32_t tdls_peer_reset_discovery_processed( + struct tdls_vdev_priv_obj *tdls_vdev) +{ + int i; + qdf_list_t *head; + qdf_list_node_t *p_node; + struct tdls_peer *peer; + QDF_STATUS status; + + tdls_vdev->discovery_peer_cnt = 0; + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &tdls_vdev->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); + peer->discovery_processed = 0; + status = qdf_list_peek_next(head, p_node, &p_node); + } + } + + return 0; +} + +void tdls_discovery_timeout_peer_cb(void *user_data) +{ + int i; + qdf_list_t *head; + qdf_list_node_t *p_node; + struct tdls_peer *peer; + QDF_STATUS status; + struct tdls_vdev_priv_obj *tdls_vdev; + + if (!user_data) { + tdls_err("discovery time out data is null"); + return; + } + + tdls_vdev = (struct tdls_vdev_priv_obj *) user_data; + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &tdls_vdev->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 (TDLS_LINK_DISCOVERING != peer->link_status) { + status = qdf_list_peek_next(head, p_node, + &p_node); + continue; + } + tdls_debug(QDF_MAC_ADDR_STR " to idle state", + QDF_MAC_ADDR_ARRAY(peer->peer_mac.bytes)); + tdls_set_peer_link_status(peer, + TDLS_LINK_IDLE, + TDLS_LINK_NOT_SUPPORTED); + } + } + tdls_vdev->discovery_sent_cnt = 0; + + /* add tdls power save prohibited */ + + return; +} + +/** + * tdls_reset_tx_rx() - reset tx/rx counters for all tdls peers + * @tdls_vdev: TDLS vdev object + * + * Caller has to take the TDLS lock before calling this function + * + * Return: Void + */ +static void tdls_reset_tx_rx(struct tdls_vdev_priv_obj *tdls_vdev) +{ + int i; + qdf_list_t *head; + qdf_list_node_t *p_node; + struct tdls_peer *peer; + QDF_STATUS status; + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &tdls_vdev->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); + peer->tx_pkt = 0; + peer->rx_pkt = 0; + status = qdf_list_peek_next(head, p_node, &p_node); + } + } + return; +} + +void tdls_implicit_disable(struct tdls_vdev_priv_obj *tdls_vdev) +{ + tdls_debug("Disable Implicit TDLS"); + tdls_timers_stop(tdls_vdev); +} + +/** + * tdls_implicit_enable() - enable implicit tdls triggering + * @tdls_vdev: TDLS vdev + * + * Return: Void + */ +void tdls_implicit_enable(struct tdls_vdev_priv_obj *tdls_vdev) +{ + tdls_debug("Enable Implicit TDLS"); + if (!tdls_vdev) + return; + + tdls_peer_reset_discovery_processed(tdls_vdev); + tdls_reset_tx_rx(tdls_vdev); + /* TODO check whether tdls power save prohibited */ + + /* Restart the connection tracker timer */ + tdls_timer_restart(tdls_vdev->vdev, &tdls_vdev->peer_update_timer, + tdls_vdev->threshold_config.tx_period_t); +} + +/** + * tdls_ct_sampling_tx_rx() - collect tx/rx traffic sample + * @tdls_vdev_obj: tdls vdev object + * @tdls_soc_obj: tdls soc object + * + * Function to update data traffic information in tdls connection + * tracker data structure for connection tracker operation + * + * Return: None + */ +static void tdls_ct_sampling_tx_rx(struct tdls_vdev_priv_obj *tdls_vdev, + struct tdls_soc_priv_obj *tdls_soc) +{ + struct tdls_peer *curr_peer; + uint8_t mac[QDF_MAC_ADDR_SIZE]; + uint8_t mac_cnt; + uint8_t mac_entries; + struct tdls_conn_tracker_mac_table mac_table[WLAN_TDLS_CT_TABLE_SIZE]; + + qdf_spin_lock_bh(&tdls_soc->tdls_ct_spinlock); + + if (0 == tdls_vdev->valid_mac_entries) { + qdf_spin_unlock_bh(&tdls_soc->tdls_ct_spinlock); + return; + } + + mac_entries = QDF_MIN(tdls_vdev->valid_mac_entries, + WLAN_TDLS_CT_TABLE_SIZE); + + qdf_mem_copy(mac_table, tdls_vdev->ct_peer_table, + (sizeof(struct tdls_conn_tracker_mac_table)) * mac_entries); + + qdf_mem_set(tdls_vdev->ct_peer_table, 0, + (sizeof(struct tdls_conn_tracker_mac_table)) * mac_entries); + + tdls_vdev->valid_mac_entries = 0; + + qdf_spin_unlock_bh(&tdls_soc->tdls_ct_spinlock); + + for (mac_cnt = 0; mac_cnt < mac_entries; mac_cnt++) { + qdf_mem_copy(mac, mac_table[mac_cnt].mac_address.bytes, + QDF_MAC_ADDR_SIZE); + curr_peer = tdls_get_peer(tdls_vdev, mac); + if (NULL != curr_peer) { + curr_peer->tx_pkt = + mac_table[mac_cnt].tx_packet_cnt; + curr_peer->rx_pkt = + mac_table[mac_cnt].rx_packet_cnt; + } + } +} + +void tdls_update_rx_pkt_cnt(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *mac_addr) +{ + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct tdls_soc_priv_obj *tdls_soc_obj; + uint8_t mac_cnt; + uint8_t valid_mac_entries; + struct tdls_conn_tracker_mac_table *mac_table; + + if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(vdev, &tdls_vdev_obj, + &tdls_soc_obj)) + return; + + if (!tdls_soc_obj->enable_tdls_connection_tracker) + return; + + if (qdf_is_macaddr_group(mac_addr)) + return; + + if (qdf_mem_cmp(vdev->vdev_mlme.macaddr, mac_addr, + QDF_MAC_ADDR_SIZE) == 0) + return; + + qdf_spin_lock_bh(&tdls_soc_obj->tdls_ct_spinlock); + valid_mac_entries = tdls_vdev_obj->valid_mac_entries; + mac_table = tdls_vdev_obj->ct_peer_table; + + for (mac_cnt = 0; mac_cnt < valid_mac_entries; mac_cnt++) { + if (qdf_mem_cmp(mac_table[mac_cnt].mac_address.bytes, + mac_addr, QDF_MAC_ADDR_SIZE) == 0) { + mac_table[mac_cnt].rx_packet_cnt++; + goto rx_cnt_return; + } + } + + /* If we have more than 8 peers within 30 mins. we will + * stop tracking till the old entries are removed + */ + if (mac_cnt < WLAN_TDLS_CT_TABLE_SIZE) { + qdf_mem_copy(mac_table[mac_cnt].mac_address.bytes, + mac_addr, QDF_MAC_ADDR_SIZE); + tdls_vdev_obj->valid_mac_entries = mac_cnt+1; + mac_table[mac_cnt].rx_packet_cnt = 1; + } + +rx_cnt_return: + qdf_spin_unlock_bh(&tdls_soc_obj->tdls_ct_spinlock); + return; +} + +void tdls_update_tx_pkt_cnt(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *mac_addr) +{ + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct tdls_soc_priv_obj *tdls_soc_obj; + uint8_t mac_cnt; + uint8_t valid_mac_entries; + struct tdls_conn_tracker_mac_table *mac_table; + + if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(vdev, &tdls_vdev_obj, + &tdls_soc_obj)) + return; + + if (!tdls_soc_obj->enable_tdls_connection_tracker) + return; + + if (qdf_is_macaddr_group(mac_addr)) + return; + + if (qdf_mem_cmp(vdev->vdev_mlme.macaddr, mac_addr, + QDF_MAC_ADDR_SIZE) == 0) + return; + + qdf_spin_lock_bh(&tdls_soc_obj->tdls_ct_spinlock); + mac_table = tdls_vdev_obj->ct_peer_table; + + valid_mac_entries = tdls_vdev_obj->valid_mac_entries; + + for (mac_cnt = 0; mac_cnt < valid_mac_entries; mac_cnt++) { + if (qdf_mem_cmp(mac_table[mac_cnt].mac_address.bytes, + mac_addr, QDF_MAC_ADDR_SIZE) == 0) { + mac_table[mac_cnt].tx_packet_cnt++; + goto tx_cnt_return; + } + } + + /* If we have more than 8 peers within 30 mins. we will + * stop tracking till the old entries are removed + */ + if (mac_cnt < WLAN_TDLS_CT_TABLE_SIZE) { + qdf_mem_copy(mac_table[mac_cnt].mac_address.bytes, + mac_addr, QDF_MAC_ADDR_SIZE); + mac_table[mac_cnt].tx_packet_cnt = 1; + tdls_vdev_obj->valid_mac_entries++; + } + +tx_cnt_return: + qdf_spin_unlock_bh(&tdls_soc_obj->tdls_ct_spinlock); + return; +} + +void tdls_implicit_send_discovery_request( + struct tdls_vdev_priv_obj *tdls_vdev_obj) +{ + struct tdls_peer *curr_peer; + struct tdls_peer *temp_peer; + struct tdls_soc_priv_obj *tdls_psoc; + struct tdls_osif_indication tdls_ind; + + if (NULL == tdls_vdev_obj) { + tdls_notice("tdls_vdev_obj is NULL"); + return; + } + + tdls_psoc = wlan_vdev_get_tdls_soc_obj(tdls_vdev_obj->vdev); + + if (NULL == tdls_psoc) { + tdls_notice("tdls_psoc_obj is NULL"); + return; + } + + curr_peer = tdls_vdev_obj->curr_candidate; + + if (NULL == curr_peer) { + tdls_err("curr_peer is NULL"); + return; + } + + /* This function is called in mutex_lock */ + temp_peer = tdls_is_progress(tdls_vdev_obj, NULL, 0); + if (NULL != temp_peer) { + tdls_notice(QDF_MAC_ADDR_STR " ongoing. pre_setup ignored", + QDF_MAC_ADDR_ARRAY(temp_peer->peer_mac.bytes)); + goto done; + } + + if (TDLS_CAP_UNKNOWN != curr_peer->tdls_support) + tdls_set_peer_link_status(curr_peer, + TDLS_LINK_DISCOVERING, + TDLS_LINK_SUCCESS); + + qdf_mem_copy(tdls_ind.peer_mac, curr_peer->peer_mac.bytes, + QDF_MAC_ADDR_SIZE); + + tdls_ind.vdev = tdls_vdev_obj->vdev; + + tdls_debug("Implicit TDLS, Send Discovery request event"); + + tdls_psoc->tdls_event_cb(tdls_psoc->tdls_evt_cb_data, + TDLS_EVENT_DISCOVERY_REQ, &tdls_ind); + + tdls_vdev_obj->discovery_sent_cnt++; + + tdls_timer_restart(tdls_vdev_obj->vdev, + &tdls_vdev_obj->peer_discovery_timer, + tdls_vdev_obj->threshold_config.tx_period_t - + TDLS_DISCOVERY_TIMEOUT_ERE_UPDATE); + + tdls_debug("discovery count %u timeout %u msec", + tdls_vdev_obj->discovery_sent_cnt, + tdls_vdev_obj->threshold_config.tx_period_t - + TDLS_DISCOVERY_TIMEOUT_ERE_UPDATE); +done: + tdls_vdev_obj->curr_candidate = NULL; + tdls_vdev_obj->magic = 0; + return; +} + +int tdls_recv_discovery_resp(struct tdls_vdev_priv_obj *tdls_vdev, + const uint8_t *mac) +{ + struct tdls_peer *curr_peer; + struct tdls_soc_priv_obj *tdls_soc; + struct tdls_osif_indication indication; + struct tdls_config_params *tdls_cfg; + int status = 0; + + if (!tdls_vdev) + return -EINVAL; + + tdls_soc = wlan_vdev_get_tdls_soc_obj(tdls_vdev->vdev); + if (NULL == tdls_soc) { + tdls_err("tdls soc is NULL"); + return -EINVAL; + } + + curr_peer = tdls_get_peer(tdls_vdev, mac); + if (NULL == curr_peer) { + tdls_err("curr_peer is NULL"); + return -EINVAL; + } + + if (tdls_vdev->discovery_sent_cnt) + tdls_vdev->discovery_sent_cnt--; + + if (0 == tdls_vdev->discovery_sent_cnt) + qdf_mc_timer_stop(&tdls_vdev->peer_discovery_timer); + + tdls_debug("Discovery(%u) Response from " QDF_MAC_ADDR_STR + " link_status %d", tdls_vdev->discovery_sent_cnt, + QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes), + curr_peer->link_status); + + tdls_cfg = &tdls_vdev->threshold_config; + if (TDLS_LINK_DISCOVERING == curr_peer->link_status) { + /* Since we are here, it means Throughput threshold is + * already met. Make sure RSSI threshold is also met + * before setting up TDLS link. + */ + if ((int32_t) curr_peer->rssi > + (int32_t) tdls_cfg->rssi_trigger_threshold) { + tdls_set_peer_link_status(curr_peer, + TDLS_LINK_DISCOVERED, + TDLS_LINK_SUCCESS); + tdls_debug("Rssi Threshold met: " QDF_MAC_ADDR_STR + " rssi = %d threshold= %d", + QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes), + curr_peer->rssi, + tdls_cfg->rssi_trigger_threshold); + + qdf_mem_copy(indication.peer_mac, mac, + QDF_MAC_ADDR_SIZE); + + indication.vdev = tdls_vdev->vdev; + + tdls_soc->tdls_event_cb(tdls_soc->tdls_evt_cb_data, + TDLS_EVENT_SETUP_REQ, + &indication); + } else { + tdls_debug("Rssi Threshold not met: " QDF_MAC_ADDR_STR + " rssi = %d threshold = %d ", + QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes), + curr_peer->rssi, + tdls_cfg->rssi_trigger_threshold); + + tdls_set_peer_link_status(curr_peer, + TDLS_LINK_IDLE, + TDLS_LINK_UNSPECIFIED); + + /* if RSSI threshold is not met then allow + * further discovery attempts by decrementing + * count for the last attempt + */ + if (curr_peer->discovery_attempt) + curr_peer->discovery_attempt--; + } + } + + curr_peer->tdls_support = TDLS_CAP_SUPPORTED; + + return status; +} + +void tdls_indicate_teardown(struct tdls_vdev_priv_obj *tdls_vdev, + struct tdls_peer *curr_peer, + uint16_t reason) +{ + struct tdls_soc_priv_obj *tdls_soc; + struct tdls_osif_indication indication; + + if (!tdls_vdev || !curr_peer) { + tdls_err("tdls_vdev: %pK, curr_peer: %pK", + tdls_vdev, curr_peer); + return; + } + + tdls_soc = wlan_vdev_get_tdls_soc_obj(tdls_vdev->vdev); + if (!tdls_soc) { + tdls_err("tdls_soc: %pK", tdls_soc); + return; + } + + if (TDLS_LINK_CONNECTED != curr_peer->link_status) + return; + + tdls_set_peer_link_status(curr_peer, + TDLS_LINK_TEARING, + TDLS_LINK_UNSPECIFIED); + tdls_notice("Teardown reason %d", reason); + + if (tdls_soc->tdls_dp_vdev_update) + tdls_soc->tdls_dp_vdev_update(&tdls_soc->soc, + curr_peer->sta_id, + tdls_soc->tdls_update_dp_vdev_flags, + false); + + indication.reason = reason; + indication.vdev = tdls_vdev->vdev; + qdf_mem_copy(indication.peer_mac, curr_peer->peer_mac.bytes, + QDF_MAC_ADDR_SIZE); + + if (tdls_soc->tdls_event_cb) + tdls_soc->tdls_event_cb(tdls_soc->tdls_evt_cb_data, + TDLS_EVENT_TEARDOWN_REQ, &indication); +} + +/** + * tdls_get_conn_info() - get the tdls connection information. + * @tdls_soc: tdls soc object + * @idx: sta id + * + * Function to check tdls sta index + * + * Return: tdls connection information + */ +static struct tdls_conn_info * +tdls_get_conn_info(struct tdls_soc_priv_obj *tdls_soc, uint8_t idx) +{ + uint8_t sta_idx; + + /* check if there is available index for this new TDLS STA */ + for (sta_idx = 0; sta_idx < WLAN_TDLS_STA_MAX_NUM; sta_idx++) { + if (idx == tdls_soc->tdls_conn_info[sta_idx].sta_id) { + tdls_debug("tdls peer with sta_idx %u exists", idx); + return &tdls_soc->tdls_conn_info[sta_idx]; + } + } + + tdls_err("tdls peer with staIdx %u not exists", idx); + return NULL; +} + +static void +tdls_ct_process_idle_handler( + struct tdls_ct_idle_peer_data *tdls_idle_peer_data) +{ + struct tdls_conn_info *tdls_info; + struct tdls_peer *curr_peer; + struct wlan_objmgr_vdev *vdev; + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct tdls_soc_priv_obj *tdls_soc_obj; + + vdev = tdls_idle_peer_data->vdev; + tdls_info = tdls_idle_peer_data->tdls_info; + + if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(vdev, &tdls_vdev_obj, + &tdls_soc_obj)) + return; + + if (INVALID_TDLS_PEER_ID == tdls_info->sta_id) { + tdls_err("peer (staidx %u) doesn't exists", tdls_info->sta_id); + return; + } + + curr_peer = tdls_find_peer(tdls_vdev_obj, + (u8 *) &tdls_info->peer_mac.bytes[0]); + + if (NULL == curr_peer) { + tdls_err("Invalid tdls idle timer expired"); + return; + } + + tdls_debug(QDF_MAC_ADDR_STR + " tx_pkt: %d, rx_pkt: %d, idle_packet_n: %d", + QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes), + curr_peer->tx_pkt, + curr_peer->rx_pkt, + tdls_vdev_obj->threshold_config.idle_packet_n); + + /* Check tx/rx statistics on this tdls link for recent activities and + * then decide whether to tear down the link or keep it. + */ + if ((curr_peer->tx_pkt >= + tdls_vdev_obj->threshold_config.idle_packet_n) || + (curr_peer->rx_pkt >= + tdls_vdev_obj->threshold_config.idle_packet_n)) { + /* this tdls link got back to normal, so keep it */ + tdls_debug("tdls link to " QDF_MAC_ADDR_STR + " back to normal, will stay", + QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes)); + } else { + /* this tdls link needs to get torn down */ + tdls_notice("trigger tdls link to "QDF_MAC_ADDR_STR" down", + QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes)); + tdls_indicate_teardown(tdls_vdev_obj, + curr_peer, + TDLS_TEARDOWN_PEER_UNSPEC_REASON); + } + + return; +} + +void tdls_ct_idle_handler(void *user_data) +{ + struct tdls_ct_idle_peer_data *tdls_idle_peer_data; + struct wlan_objmgr_vdev *vdev; + + tdls_idle_peer_data = (struct tdls_ct_idle_peer_data *) user_data; + + if (NULL == tdls_idle_peer_data || + NULL == tdls_idle_peer_data->vdev || + NULL == tdls_idle_peer_data->tdls_info) + return; + + vdev = tdls_idle_peer_data->vdev; + if (QDF_STATUS_SUCCESS != wlan_objmgr_vdev_try_get_ref(vdev, + WLAN_TDLS_NB_ID)) + return; + + tdls_ct_process_idle_handler(tdls_idle_peer_data); + wlan_objmgr_vdev_release_ref(vdev, + WLAN_TDLS_NB_ID); +} + +/** + * tdls_ct_process_idle_and_discovery() - process the traffic data + * @curr_peer: tdls peer needs to be examined + * @tdls_vdev_obj: tdls vdev object + * @tdls_soc_obj: tdls soc object + * + * Function to check the peer traffic data in idle link and tdls + * discovering link + * + * Return: None + */ +static void +tdls_ct_process_idle_and_discovery(struct tdls_peer *curr_peer, + struct tdls_vdev_priv_obj *tdls_vdev_obj, + struct tdls_soc_priv_obj *tdls_soc_obj) +{ + uint16_t valid_peers; + + valid_peers = tdls_soc_obj->connected_peer_count; + + if ((curr_peer->tx_pkt + curr_peer->rx_pkt) >= + tdls_vdev_obj->threshold_config.tx_packet_n) { + if (WLAN_TDLS_STA_MAX_NUM > valid_peers) { + tdls_notice("Tput trigger TDLS pre-setup"); + tdls_vdev_obj->curr_candidate = curr_peer; + tdls_implicit_send_discovery_request(tdls_vdev_obj); + } else { + tdls_notice("Maximum peers connected already! %d", + valid_peers); + } + } +} + +/** + * tdls_ct_process_connected_link() - process the traffic + * @curr_peer: tdls peer needs to be examined + * @tdls_vdev_obj: tdls vdev + * @tdls_soc_obj: tdls soc context + * + * Function to check the peer traffic data in active STA + * session + * + * Return: None + */ +static void tdls_ct_process_connected_link( + struct tdls_peer *curr_peer, + struct tdls_vdev_priv_obj *tdls_vdev, + struct tdls_soc_priv_obj *tdls_soc) +{ + + if ((int32_t)curr_peer->rssi < + (int32_t)tdls_vdev->threshold_config.rssi_teardown_threshold) { + tdls_warn("Tear down - low RSSI: " QDF_MAC_ADDR_STR "!", + QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes)); + tdls_indicate_teardown(tdls_vdev, + curr_peer, + TDLS_TEARDOWN_PEER_UNSPEC_REASON); + return; + } + + /* Only teardown based on non zero idle packet threshold, to address + * a use case where this threshold does not get consider for TEAR DOWN + */ + if ((0 != tdls_vdev->threshold_config.idle_packet_n) && + ((curr_peer->tx_pkt < + tdls_vdev->threshold_config.idle_packet_n) && + (curr_peer->rx_pkt < + tdls_vdev->threshold_config.idle_packet_n))) { + if (!curr_peer->is_peer_idle_timer_initialised) { + uint8_t sta_id = (uint8_t)curr_peer->sta_id; + struct tdls_conn_info *tdls_info; + tdls_info = tdls_get_conn_info(tdls_soc, sta_id); + tdls_soc->tdls_idle_peer_data.tdls_info = tdls_info; + tdls_soc->tdls_idle_peer_data.vdev = tdls_vdev->vdev; + qdf_mc_timer_init(&curr_peer->peer_idle_timer, + QDF_TIMER_TYPE_SW, + tdls_ct_idle_handler, + &tdls_soc->tdls_idle_peer_data); + curr_peer->is_peer_idle_timer_initialised = true; + } + if (QDF_TIMER_STATE_RUNNING != + curr_peer->peer_idle_timer.state) { + tdls_warn("Tx/Rx Idle timer start: " + QDF_MAC_ADDR_STR "!", + QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes)); + tdls_timer_restart(tdls_vdev->vdev, + &curr_peer->peer_idle_timer, + tdls_vdev->threshold_config.idle_timeout_t); + } + } else if (QDF_TIMER_STATE_RUNNING == + curr_peer->peer_idle_timer.state) { + tdls_warn("Tx/Rx Idle timer stop: " QDF_MAC_ADDR_STR "!", + QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes)); + qdf_mc_timer_stop(&curr_peer->peer_idle_timer); + } +} + +/** + * tdls_ct_process_cap_supported() - process TDLS supported peer. + * @curr_peer: tdls peer needs to be examined + * @tdls_vdev_obj: tdls vdev context + * @tdls_soc_obj: tdls soc context + * + * Function to check the peer traffic data for tdls supported peer + * + * Return: None + */ +static void tdls_ct_process_cap_supported(struct tdls_peer *curr_peer, + struct tdls_vdev_priv_obj *tdls_vdev, + struct tdls_soc_priv_obj *tdls_soc_obj) +{ + tdls_debug("tx %d rx %d thr.pkt %d/idle %d rssi %d thr.trig %d/tear %d", + curr_peer->tx_pkt, curr_peer->rx_pkt, + tdls_vdev->threshold_config.tx_packet_n, + tdls_vdev->threshold_config.idle_packet_n, + curr_peer->rssi, + tdls_vdev->threshold_config.rssi_trigger_threshold, + tdls_vdev->threshold_config.rssi_teardown_threshold); + + switch (curr_peer->link_status) { + case TDLS_LINK_IDLE: + case TDLS_LINK_DISCOVERING: + if (TDLS_IS_EXTERNAL_CONTROL_ENABLED( + tdls_soc_obj->tdls_configs.tdls_feature_flags) && + (!curr_peer->is_forced_peer)) + break; + tdls_ct_process_idle_and_discovery(curr_peer, tdls_vdev, + tdls_soc_obj); + break; + case TDLS_LINK_CONNECTED: + tdls_ct_process_connected_link(curr_peer, tdls_vdev, + tdls_soc_obj); + break; + default: + break; + } +} + +/** + * tdls_ct_process_cap_unknown() - process unknown peer + * @curr_peer: tdls peer needs to be examined + * @tdls_vdev_obj: tdls vdev object + * @tdls_soc_obj: tdls soc object + * + * Function check the peer traffic data , when tdls capability is unknown + * + * Return: None + */ +static void tdls_ct_process_cap_unknown(struct tdls_peer *curr_peer, + struct tdls_vdev_priv_obj *tdls_vdev, + struct tdls_soc_priv_obj *tdlsa_soc) +{ + if (TDLS_IS_EXTERNAL_CONTROL_ENABLED( + tdlsa_soc->tdls_configs.tdls_feature_flags) && + (!curr_peer->is_forced_peer)) + return; + + tdls_debug("threshold tx pkt = %d peer tx_pkt = %d & rx_pkt = %d ", + tdls_vdev->threshold_config.tx_packet_n, curr_peer->tx_pkt, + curr_peer->rx_pkt); + + if (!TDLS_IS_LINK_CONNECTED(curr_peer) && + ((curr_peer->tx_pkt + curr_peer->rx_pkt) >= + tdls_vdev->threshold_config.tx_packet_n)) { + /* Ignore discovery attempt if External Control is enabled, that + * is, peer is forced. In that case, continue discovery attempt + * regardless attempt count + */ + tdls_debug("TDLS UNKNOWN pre discover "); + if (curr_peer->is_forced_peer || + curr_peer->discovery_attempt++ < + tdls_vdev->threshold_config.discovery_tries_n) { + tdls_debug("TDLS UNKNOWN discover "); + tdls_vdev->curr_candidate = curr_peer; + tdls_implicit_send_discovery_request(tdls_vdev); + } else { + curr_peer->tdls_support = TDLS_CAP_NOT_SUPPORTED; + tdls_set_peer_link_status( + curr_peer, + TDLS_LINK_IDLE, + TDLS_LINK_NOT_SUPPORTED); + } + } +} + +/** + * tdls_ct_process_peers() - process the peer + * @curr_peer: tdls peer needs to be examined + * @tdls_vdev_obj: tdls vdev object + * @tdls_soc_obj: tdls soc object + * + * This function check the peer capability and process the metadata from + * the peer + * + * Return: None + */ +static void tdls_ct_process_peers(struct tdls_peer *curr_peer, + struct tdls_vdev_priv_obj *tdls_vdev_obj, + struct tdls_soc_priv_obj *tdls_soc_obj) +{ + tdls_debug(QDF_MAC_ADDR_STR " link_status %d tdls_support %d", + QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes), + curr_peer->link_status, curr_peer->tdls_support); + + switch (curr_peer->tdls_support) { + case TDLS_CAP_SUPPORTED: + tdls_ct_process_cap_supported(curr_peer, tdls_vdev_obj, + tdls_soc_obj); + break; + + case TDLS_CAP_UNKNOWN: + tdls_ct_process_cap_unknown(curr_peer, tdls_vdev_obj, + tdls_soc_obj); + break; + default: + break; + } + +} + +static void tdls_ct_process_handler(struct wlan_objmgr_vdev *vdev) +{ + int i; + qdf_list_t *head; + qdf_list_node_t *list_node; + struct tdls_peer *curr_peer; + QDF_STATUS status; + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct tdls_soc_priv_obj *tdls_soc_obj; + + if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(vdev, &tdls_vdev_obj, + &tdls_soc_obj)) + return; + + /* If any concurrency is detected */ + if (!tdls_soc_obj->enable_tdls_connection_tracker) { + tdls_notice("Connection tracker is disabled"); + return; + } + + /* Update tx rx traffic sample in tdls data structures */ + tdls_ct_sampling_tx_rx(tdls_vdev_obj, tdls_soc_obj); + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &tdls_vdev_obj->peer_list[i]; + status = qdf_list_peek_front(head, &list_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + curr_peer = qdf_container_of(list_node, + struct tdls_peer, node); + tdls_ct_process_peers(curr_peer, tdls_vdev_obj, + tdls_soc_obj); + curr_peer->tx_pkt = 0; + curr_peer->rx_pkt = 0; + status = qdf_list_peek_next(head, + list_node, &list_node); + } + } + + tdls_timer_restart(tdls_vdev_obj->vdev, + &tdls_vdev_obj->peer_update_timer, + tdls_vdev_obj->threshold_config.tx_period_t); + +} + +void tdls_ct_handler(void *user_data) +{ + struct wlan_objmgr_vdev *vdev; + + if (!user_data) + return; + + vdev = (struct wlan_objmgr_vdev *)user_data; + + if (QDF_STATUS_SUCCESS != wlan_objmgr_vdev_try_get_ref(vdev, + WLAN_TDLS_NB_ID)) + return; + + tdls_ct_process_handler(vdev); + + wlan_objmgr_vdev_release_ref(vdev, + WLAN_TDLS_NB_ID); +} + +int tdls_set_tdls_offchannel(struct tdls_soc_priv_obj *tdls_soc, + int offchannel) +{ + uint32_t tdls_feature_flags; + + tdls_feature_flags = tdls_soc->tdls_configs.tdls_feature_flags; + + if (TDLS_IS_OFF_CHANNEL_ENABLED(tdls_feature_flags) && + (TDLS_SUPPORT_EXP_TRIG_ONLY == tdls_soc->tdls_current_mode || + TDLS_SUPPORT_IMP_MODE == tdls_soc->tdls_current_mode || + TDLS_SUPPORT_EXT_CONTROL == tdls_soc->tdls_current_mode)) { + if (offchannel < TDLS_PREFERRED_OFF_CHANNEL_NUM_MIN || + offchannel > TDLS_PREFERRED_OFF_CHANNEL_NUM_MAX) { + tdls_err("Invalid tdls off channel %u", offchannel); + return -EINVAL; + } + } else { + tdls_err("Either TDLS or TDLS Off-channel is not enabled"); + return -ENOTSUPP; + } + tdls_notice("change tdls off channel from %d to %d", + tdls_soc->tdls_off_channel, offchannel); + tdls_soc->tdls_off_channel = offchannel; + return 0; +} + +int tdls_set_tdls_secoffchanneloffset(struct tdls_soc_priv_obj *tdls_soc, + int offchanoffset) +{ + uint32_t tdls_feature_flags; + + tdls_feature_flags = tdls_soc->tdls_configs.tdls_feature_flags; + + if (!TDLS_IS_OFF_CHANNEL_ENABLED(tdls_feature_flags) || + TDLS_SUPPORT_SUSPENDED >= tdls_soc->tdls_current_mode) { + tdls_err("Either TDLS or TDLS Off-channel is not enabled"); + return -ENOTSUPP; + } + + tdls_soc->tdls_channel_offset = BW_INVALID; + + switch (offchanoffset) { + case TDLS_SEC_OFFCHAN_OFFSET_0: + tdls_soc->tdls_channel_offset = BW20; + break; + case TDLS_SEC_OFFCHAN_OFFSET_40PLUS: + tdls_soc->tdls_channel_offset = BW40_LOW_PRIMARY; + break; + case TDLS_SEC_OFFCHAN_OFFSET_40MINUS: + tdls_soc->tdls_channel_offset = BW40_LOW_PRIMARY; + break; + case TDLS_SEC_OFFCHAN_OFFSET_80: + tdls_soc->tdls_channel_offset = BW80; + break; + case TDLS_SEC_OFFCHAN_OFFSET_160: + tdls_soc->tdls_channel_offset = BWALL; + break; + default: + tdls_err("Invalid tdls secondary off channel offset %d", + offchanoffset); + return -EINVAL; + } /* end switch */ + + tdls_notice("change tdls secondary off channel offset to 0x%x", + tdls_soc->tdls_channel_offset); + return 0; +} + +int tdls_set_tdls_offchannelmode(struct wlan_objmgr_vdev *vdev, + int offchanmode) +{ + struct tdls_peer *conn_peer = NULL; + struct tdls_channel_switch_params chan_switch_params; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + int ret_value = 0; + struct tdls_vdev_priv_obj *tdls_vdev; + struct tdls_soc_priv_obj *tdls_soc; + uint32_t tdls_feature_flags; + + + status = tdls_get_vdev_objects(vdev, &tdls_vdev, &tdls_soc); + + if (status != QDF_STATUS_SUCCESS) + return -EINVAL; + + + if (offchanmode < ENABLE_CHANSWITCH || + offchanmode > DISABLE_CHANSWITCH) { + tdls_err("Invalid tdls off channel mode %d", offchanmode); + return -EINVAL; + } + + if (!tdls_is_vdev_connected(vdev)) { + tdls_err("tdls off channel req in not associated state %d", + offchanmode); + return -EPERM; + } + + tdls_feature_flags = tdls_soc->tdls_configs.tdls_feature_flags; + if (!TDLS_IS_OFF_CHANNEL_ENABLED(tdls_feature_flags) || + TDLS_SUPPORT_SUSPENDED >= tdls_soc->tdls_current_mode) { + tdls_err("Either TDLS or TDLS Off-channel is not enabled"); + return -ENOTSUPP; + } + + conn_peer = tdls_find_first_connected_peer(tdls_vdev); + if (NULL == conn_peer) { + tdls_err("No TDLS Connected Peer"); + return -EPERM; + } + + tdls_notice("TDLS Channel Switch in swmode=%d tdls_off_channel %d offchanoffset %d", + offchanmode, tdls_soc->tdls_off_channel, + tdls_soc->tdls_channel_offset); + + switch (offchanmode) { + case ENABLE_CHANSWITCH: + if (tdls_soc->tdls_off_channel && + tdls_soc->tdls_channel_offset != BW_INVALID) { + chan_switch_params.tdls_off_ch = + tdls_soc->tdls_off_channel; + chan_switch_params.tdls_off_ch_bw_offset = + tdls_soc->tdls_channel_offset; + chan_switch_params.oper_class = + tdls_find_opclass(tdls_soc->soc, + chan_switch_params.tdls_off_ch, + chan_switch_params.tdls_off_ch_bw_offset); + } else { + tdls_err("TDLS off-channel parameters are not set yet!!!"); + return -EINVAL; + + } + break; + case DISABLE_CHANSWITCH: + chan_switch_params.tdls_off_ch = 0; + chan_switch_params.tdls_off_ch_bw_offset = 0; + chan_switch_params.oper_class = 0; + break; + default: + tdls_err("Incorrect Parameters mode: %d tdls_off_channel: %d offchanoffset: %d", + offchanmode, tdls_soc->tdls_off_channel, + tdls_soc->tdls_channel_offset); + return -EINVAL; + } /* end switch */ + + chan_switch_params.vdev_id = tdls_vdev->session_id; + chan_switch_params.tdls_sw_mode = offchanmode; + chan_switch_params.is_responder = + conn_peer->is_responder; + qdf_mem_copy(&chan_switch_params.peer_mac_addr, + &conn_peer->peer_mac.bytes, + QDF_MAC_ADDR_SIZE); + tdls_notice("Peer " QDF_MAC_ADDR_STR " vdevId: %d, off channel: %d, offset: %d, mode: %d, is_responder: %d", + QDF_MAC_ADDR_ARRAY(chan_switch_params.peer_mac_addr), + chan_switch_params.vdev_id, + chan_switch_params.tdls_off_ch, + chan_switch_params.tdls_off_ch_bw_offset, + chan_switch_params.tdls_sw_mode, + chan_switch_params.is_responder); + + status = tdls_set_offchan_mode(tdls_soc->soc, + &chan_switch_params); + + if (status != QDF_STATUS_SUCCESS) { + tdls_err("Failed to send channel switch request to wmi"); + return -EINVAL; + } + + tdls_soc->tdls_fw_off_chan_mode = offchanmode; + + if (ENABLE_CHANSWITCH == offchanmode) { + conn_peer = tdls_find_first_connected_peer(tdls_vdev); + if (NULL == conn_peer) { + tdls_err("No TDLS Connected Peer"); + return -EPERM; + } + conn_peer->pref_off_chan_num = + chan_switch_params.tdls_off_ch; + conn_peer->op_class_for_pref_off_chan = + chan_switch_params.oper_class; + } + + return ret_value; +} + +static QDF_STATUS tdls_delete_all_tdls_peers_flush_cb(struct scheduler_msg *msg) +{ + if (msg && msg->bodyptr) { + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + } + + return QDF_STATUS_SUCCESS; +} +/** + * tdls_delete_all_tdls_peers(): send request to delete tdls peers + * @vdev: vdev object + * @tdls_soc: tdls soc object + * + * This function sends request to lim to delete tdls peers + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_delete_all_tdls_peers(struct wlan_objmgr_vdev *vdev, + struct tdls_soc_priv_obj *tdls_soc) +{ + struct wlan_objmgr_peer *peer; + struct tdls_del_all_tdls_peers *del_msg; + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + + del_msg = qdf_mem_malloc(sizeof(*del_msg)); + if (NULL == del_msg) { + tdls_err("memory alloc failed"); + return QDF_STATUS_E_FAILURE; + } + qdf_mem_zero(del_msg, sizeof(*del_msg)); + + peer = wlan_vdev_get_bsspeer(vdev); + if (QDF_STATUS_SUCCESS != wlan_objmgr_peer_try_get_ref(peer, + WLAN_TDLS_SB_ID)) { + qdf_mem_free(del_msg); + return QDF_STATUS_E_FAILURE; + } + + qdf_mem_copy(del_msg->bssid.bytes, + wlan_peer_get_macaddr(peer), QDF_MAC_ADDR_SIZE); + + del_msg->msg_type = tdls_soc->tdls_del_all_peers; + del_msg->msg_len = (uint16_t) sizeof(*del_msg); + + /* Send the request to PE. */ + qdf_mem_zero(&msg, sizeof(msg)); + + tdls_debug("sending delete all peers req to PE "); + + msg.type = del_msg->msg_type; + msg.bodyptr = del_msg; + msg.flush_callback = tdls_delete_all_tdls_peers_flush_cb; + + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_PE, + QDF_MODULE_ID_PE, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("post delete all peer req failed, status %d", status); + qdf_mem_free(del_msg); + } + + wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_SB_ID); + return status; +} + +void tdls_disable_offchan_and_teardown_links( + struct wlan_objmgr_vdev *vdev) +{ + uint16_t connected_tdls_peers = 0; + uint8_t staidx; + struct tdls_peer *curr_peer = NULL; + struct tdls_vdev_priv_obj *tdls_vdev; + struct tdls_soc_priv_obj *tdls_soc; + QDF_STATUS status; + uint8_t vdev_id; + bool tdls_in_progress = false; + + status = tdls_get_vdev_objects(vdev, &tdls_vdev, &tdls_soc); + if (QDF_STATUS_SUCCESS != status) { + tdls_err("tdls objects are NULL "); + return; + } + + if (TDLS_SUPPORT_SUSPENDED >= tdls_soc->tdls_current_mode) { + tdls_notice("TDLS mode %d is disabled OR not suspended now", + tdls_soc->tdls_current_mode); + return; + } + + connected_tdls_peers = tdls_soc->connected_peer_count; + if (tdls_is_progress(tdls_vdev, NULL, 0)) + tdls_in_progress = true; + + if (!(connected_tdls_peers || tdls_in_progress)) { + tdls_notice("No TDLS connected/progress peers to delete"); + vdev_id = vdev->vdev_objmgr.vdev_id; + if (tdls_soc->set_state_info.set_state_cnt > 0) { + tdls_debug("Disable the tdls in FW as second interface is coming up"); + tdls_send_update_to_fw(tdls_vdev, tdls_soc, true, + true, false, vdev_id); + } + return; + } + + /* TDLS is not supported in case of concurrency. + * Disable TDLS Offchannel in FW to avoid more + * than two concurrent channels and generate TDLS + * teardown indication to supplicant. + * Below function Finds the first connected peer and + * disables TDLS offchannel for that peer. + * FW enables TDLS offchannel only when there is + * one TDLS peer. When there are more than one TDLS peer, + * there will not be TDLS offchannel in FW. + * So to avoid sending multiple request to FW, for now, + * just invoke offchannel mode functions only once + */ + tdls_set_tdls_offchannel(tdls_soc, + tdls_soc->tdls_configs.tdls_pre_off_chan_num); + tdls_set_tdls_secoffchanneloffset(tdls_soc, + TDLS_SEC_OFFCHAN_OFFSET_40PLUS); + tdls_set_tdls_offchannelmode(vdev, DISABLE_CHANSWITCH); + + /* Send Msg to PE for deleting all the TDLS peers */ + tdls_delete_all_tdls_peers(vdev, tdls_soc); + + for (staidx = 0; staidx < tdls_soc->max_num_tdls_sta; + staidx++) { + if (tdls_soc->tdls_conn_info[staidx].sta_id + == INVALID_TDLS_PEER_ID) + continue; + + curr_peer = tdls_find_all_peer(tdls_soc, + tdls_soc->tdls_conn_info[staidx].peer_mac.bytes); + if (!curr_peer) + continue; + + tdls_notice("indicate TDLS teardown (staId %d)", + curr_peer->sta_id); + + /* Indicate teardown to supplicant */ + tdls_indicate_teardown(tdls_vdev, + curr_peer, + TDLS_TEARDOWN_PEER_UNSPEC_REASON); + + /* + * Del Sta happened already as part of tdls_delete_all_tdls_peers + * Hence clear tdls vdev data structure. + */ + tdls_reset_peer(tdls_vdev, curr_peer->peer_mac.bytes); + + if (tdls_soc->tdls_dereg_peer) + tdls_soc->tdls_dereg_peer( + tdls_soc->tdls_peer_context, + wlan_vdev_get_id(vdev), + curr_peer->sta_id); + tdls_decrement_peer_count(tdls_soc); + tdls_soc->tdls_conn_info[staidx].sta_id = INVALID_TDLS_PEER_ID; + tdls_soc->tdls_conn_info[staidx].session_id = 255; + + qdf_mem_zero(&tdls_soc->tdls_conn_info[staidx].peer_mac, + sizeof(struct qdf_mac_addr)); + } +} + +void tdls_teardown_connections(struct wlan_objmgr_vdev *vdev) +{ + struct tdls_osif_indication indication; + struct tdls_soc_priv_obj *tdls_soc; + struct wlan_objmgr_vdev *tdls_vdev_obj; + + if (!vdev) { + QDF_ASSERT(0); + return; + } + + tdls_soc = wlan_vdev_get_tdls_soc_obj(vdev); + if (!tdls_soc) + return; + + /* Get the tdls specific vdev and clear the links */ + tdls_vdev_obj = tdls_get_vdev(tdls_soc->soc, WLAN_TDLS_SB_ID); + if (tdls_vdev_obj) { + tdls_disable_offchan_and_teardown_links(tdls_vdev_obj); + wlan_objmgr_vdev_release_ref(tdls_vdev_obj, WLAN_TDLS_SB_ID); + } + + indication.vdev = vdev; + + if (tdls_soc->tdls_event_cb) + tdls_soc->tdls_event_cb(tdls_soc->tdls_evt_cb_data, + TDLS_EVENT_TEARDOWN_LINKS_DONE, + &indication); +} diff --git a/components/tdls/core/src/wlan_tdls_ct.h b/components/tdls/core/src/wlan_tdls_ct.h new file mode 100644 index 000000000000..00e03f20d14f --- /dev/null +++ b/components/tdls/core/src/wlan_tdls_ct.h @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_ct.h + * + * TDLS connection tracker declarations + */ + +#ifndef _WLAN_TDLS_CT_H_ +#define _WLAN_TDLS_CT_H_ + + /* + * Before UpdateTimer expires, we want to timeout discovery response + * should not be more than 2000. + */ +#define TDLS_DISCOVERY_TIMEOUT_ERE_UPDATE 1000 + +#define TDLS_PREFERRED_OFF_CHANNEL_NUM_MIN 1 +#define TDLS_PREFERRED_OFF_CHANNEL_NUM_MAX 165 +#define TDLS_PREFERRED_OFF_CHANNEL_NUM_DEFAULT 36 + +/** + * tdls_is_vdev_connected() - check the vdev is connected to ap + * @vdev: vdev object manager + * + * This function will check the vdev connection status and return + * true or false + * + * Return: true - Connected, false - Not connected + */ +bool tdls_is_vdev_connected(struct wlan_objmgr_vdev *vdev); + +/** + * tdls_implicit_enable() - enable implicit tdls triggering + * @tdls_vdev: TDLS vdev + * + * Return: Void + */ +void tdls_implicit_enable(struct tdls_vdev_priv_obj *tdls_vdev); + +/** + * tdls_update_rx_pkt_cnt() - Update rx packet count + * @vdev: vdev object manager + * @mac_addr: mac address of the data + * + * Increase the rx packet count, if the sender is not bssid and the packet is + * not broadcast and multicast packet + * + * This sampling information will be used in TDLS connection tracker + * + * This function expected to be called in an atomic context so blocking APIs + * not allowed + * + * Return: None + */ +void tdls_update_rx_pkt_cnt(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *mac_addr); + +/** + * tdls_update_tx_pkt_cnt() - update tx packet + * @vdev: vdev object + * @mac_addr: mac address of the data + * + * Increase the tx packet count, if the sender is not bssid and the packet is + * not broadcast and multicast packet + * + * This sampling information will be used in TDLS connection tracker + * + * This function expected to be called in an atomic context so blocking APIs + * not allowed + * + * Return: None + */ +void tdls_update_tx_pkt_cnt(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *mac_addr); + +/** + * wlan_hdd_tdls_implicit_send_discovery_request() - send discovery request + * @tdls_vdev_obj: tdls vdev object + * + * Return: None + */ +void tdls_implicit_send_discovery_request( + struct tdls_vdev_priv_obj *tdls_vdev_obj); + +/** + * tdls_recv_discovery_resp() - handling of tdls discovery response + * @soc: object manager + * @mac: mac address of peer from which the response was received + * + * Return: 0 for success or negative errno otherwise + */ +int tdls_recv_discovery_resp(struct tdls_vdev_priv_obj *tdls_vdev, + const uint8_t *mac); + +/** + * tdls_indicate_teardown() - indicate teardown to upper layer + * @tdls_vdev: tdls vdev object + * @curr_peer: teardown peer + * @reason: teardown reason + * + * Return: Void + */ +void tdls_indicate_teardown(struct tdls_vdev_priv_obj *tdls_vdev, + struct tdls_peer *curr_peer, + uint16_t reason); + +/** + * tdls_ct_handler() - TDLS connection tracker handler + * @user_data: user data from timer + * + * tdls connection tracker timer starts, when the STA connected to AP + * and it's scan the traffic between two STA peers and make TDLS + * connection and teardown, based on the traffic threshold + * + * Return: None + */ +void tdls_ct_handler(void *user_data); + +/** + * tdls_ct_idle_handler() - Check tdls idle traffic + * @user_data: data from tdls idle timer + * + * Function to check the tdls idle traffic and make a decision about + * tdls teardown + * + * Return: None + */ +void tdls_ct_idle_handler(void *user_data); + +/** + * tdls_discovery_timeout_peer_cb() - tdls discovery timeout callback + * @userData: tdls vdev + * + * Return: None + */ +void tdls_discovery_timeout_peer_cb(void *user_data); + +/** + * tdls_implicit_disable() - disable implicit tdls triggering + * @pHddTdlsCtx: TDLS context + * + * Return: Void + */ +void tdls_implicit_disable(struct tdls_vdev_priv_obj *tdls_vdev); + +/** + * tdls_is_vdev_connected() -check the vdev connection + * @vdev: vdev oobject + * + * Return: true or false + */ +bool tdls_is_vdev_connected(struct wlan_objmgr_vdev *vdev); + +/** + * tdls_is_vdev_authenticated() -check the vdev authentication state + * @vdev: vdev oobject + * + * Return: true or false + */ +bool tdls_is_vdev_authenticated(struct wlan_objmgr_vdev *vdev); + +/** + * tdls_teardown_connections() -teardown and delete all the tdls peers + * @vdev: vdev oobject + * + * Return: true or false + */ +void tdls_teardown_connections(struct wlan_objmgr_vdev *vdev); + +/** + * tdls_disable_offchan_and_teardown_links - Disable offchannel + * and teardown TDLS links + * @tdls_soc : tdls soc object + * + * Return: None + */ +void tdls_disable_offchan_and_teardown_links( + struct wlan_objmgr_vdev *vdev); + +/** + * tdls_delete_all_tdls_peers(): send request to delete tdls peers + * @vdev: vdev object + * @tdls_soc: tdls soc object + * + * This function sends request to lim to delete tdls peers + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_delete_all_tdls_peers(struct wlan_objmgr_vdev *vdev, + struct tdls_soc_priv_obj *tdls_soc); + +/** + * tdls_set_tdls_offchannel() - set tdls off-channel number + * @tdls_soc: tdls soc object + * @offchannel: tdls off-channel number + * + * This function sets tdls off-channel number + * + * Return: 0 on success; negative errno otherwise + */ +int tdls_set_tdls_offchannel(struct tdls_soc_priv_obj *tdls_soc, + int offchannel); + +/** + * tdls_set_tdls_offchannelmode() - set tdls off-channel mode + * @adapter: Pointer to the HDD adapter + * @offchanmode: tdls off-channel mode + * + * This function sets tdls off-channel mode + * + * Return: 0 on success; negative errno otherwise + */ + +int tdls_set_tdls_offchannelmode(struct wlan_objmgr_vdev *vdev, + int offchanmode); + +/** + * tdls_set_tdls_secoffchanneloffset() - set secondary tdls off-channel offset + * @tdls_soc: tdls soc object + * @offchanoffset: tdls off-channel offset + * + * This function sets secondary tdls off-channel offset + * + * Return: 0 on success; negative errno otherwise + */ + +int tdls_set_tdls_secoffchanneloffset(struct tdls_soc_priv_obj *tdls_soc, + int offchanoffset); + +#endif diff --git a/components/tdls/core/src/wlan_tdls_main.c b/components/tdls/core/src/wlan_tdls_main.c new file mode 100644 index 000000000000..7295adda9689 --- /dev/null +++ b/components/tdls/core/src/wlan_tdls_main.c @@ -0,0 +1,1667 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_main.c + * + * TDLS core function definitions + */ + +#include "wlan_tdls_main.h" +#include "wlan_tdls_cmds_process.h" +#include "wlan_tdls_peer.h" +#include "wlan_tdls_ct.h" +#include "wlan_tdls_mgmt.h" +#include "wlan_tdls_tgt_api.h" +#include "wlan_policy_mgr_public_struct.h" +#include "wlan_policy_mgr_api.h" +#include "wlan_scan_ucfg_api.h" + + +/* Global tdls soc pvt object + * this is useful for some functions which does not receive either vdev or psoc + * objects. + */ +static struct tdls_soc_priv_obj *tdls_soc_global; + +QDF_STATUS tdls_psoc_obj_create_notification(struct wlan_objmgr_psoc *psoc, + void *arg_list) +{ + QDF_STATUS status; + struct tdls_soc_priv_obj *tdls_soc_obj; + + tdls_soc_obj = qdf_mem_malloc(sizeof(*tdls_soc_obj)); + if (!tdls_soc_obj) { + tdls_err("Failed to allocate memory for tdls object"); + return QDF_STATUS_E_NOMEM; + } + + tdls_soc_obj->soc = psoc; + + status = wlan_objmgr_psoc_component_obj_attach(psoc, + WLAN_UMAC_COMP_TDLS, + (void *)tdls_soc_obj, + QDF_STATUS_SUCCESS); + + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("Failed to attach psoc tdls component"); + qdf_mem_free(tdls_soc_obj); + return status; + } + + tdls_soc_global = tdls_soc_obj; + tdls_notice("TDLS obj attach to psoc successfully"); + + return status; +} + +QDF_STATUS tdls_psoc_obj_destroy_notification(struct wlan_objmgr_psoc *psoc, + void *arg_list) +{ + QDF_STATUS status; + struct tdls_soc_priv_obj *tdls_soc_obj; + + tdls_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_TDLS); + if (!tdls_soc_obj) { + tdls_err("Failed to get tdls obj in psoc"); + return QDF_STATUS_E_FAILURE; + } + + status = wlan_objmgr_psoc_component_obj_detach(psoc, + WLAN_UMAC_COMP_TDLS, + tdls_soc_obj); + + if (QDF_IS_STATUS_ERROR(status)) + tdls_err("Failed to detach psoc tdls component"); + qdf_mem_free(tdls_soc_obj); + + return status; +} + +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; + + 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; + } + + 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, + tdls_ct_handler, vdev_obj->vdev); + qdf_mc_timer_init(&vdev_obj->peer_discovery_timer, QDF_TIMER_TYPE_SW, + tdls_discovery_timeout_peer_cb, 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, + void *arg) +{ + QDF_STATUS status; + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct wlan_objmgr_pdev *pdev; + struct tdls_soc_priv_obj *tdls_soc_obj; + uint32_t tdls_feature_flags; + + tdls_debug("tdls vdev mode %d", wlan_vdev_mlme_get_opmode(vdev)); + if (wlan_vdev_mlme_get_opmode(vdev) != QDF_STA_MODE && + wlan_vdev_mlme_get_opmode(vdev) != QDF_P2P_CLIENT_MODE) + return QDF_STATUS_SUCCESS; + + tdls_soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (!tdls_soc_obj) { + tdls_err("get soc by vdev failed"); + return QDF_STATUS_E_NOMEM; + } + + tdls_feature_flags = tdls_soc_obj->tdls_configs.tdls_feature_flags; + if (!TDLS_IS_ENABLED(tdls_feature_flags)) { + tdls_debug("disabled in ini"); + return QDF_STATUS_E_NOSUPPORT; + } + + /* TODO: Add concurrency check */ + + tdls_vdev_obj = qdf_mem_malloc(sizeof(*tdls_vdev_obj)); + if (!tdls_vdev_obj) { + tdls_err("Failed to allocate memory for tdls vdev object"); + return QDF_STATUS_E_NOMEM; + } + + status = wlan_objmgr_vdev_component_obj_attach(vdev, + WLAN_UMAC_COMP_TDLS, + (void *)tdls_vdev_obj, + QDF_STATUS_SUCCESS); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("Failed to attach vdev tdls component"); + goto err; + } + tdls_vdev_obj->vdev = vdev; + status = tdls_vdev_init(tdls_vdev_obj); + if (QDF_IS_STATUS_ERROR(status)) + goto err; + + pdev = wlan_vdev_get_pdev(vdev); + + status = ucfg_scan_register_event_handler(pdev, + tdls_scan_complete_event_handler, + tdls_soc_obj); + + if (QDF_STATUS_SUCCESS != status) { + tdls_err("scan event register failed "); + tdls_vdev_deinit(tdls_vdev_obj); + goto err; + } + + tdls_debug("tdls object attach to vdev successfully"); + return status; +err: + qdf_mem_free(tdls_vdev_obj); + return status; +} + +QDF_STATUS tdls_vdev_obj_destroy_notification(struct wlan_objmgr_vdev *vdev, + void *arg) +{ + QDF_STATUS status; + void *tdls_vdev_obj; + struct tdls_soc_priv_obj *tdls_soc_obj; + uint32_t tdls_feature_flags; + + tdls_debug("tdls vdev mode %d", wlan_vdev_mlme_get_opmode(vdev)); + if (wlan_vdev_mlme_get_opmode(vdev) != QDF_STA_MODE && + wlan_vdev_mlme_get_opmode(vdev) != QDF_P2P_CLIENT_MODE) + return QDF_STATUS_SUCCESS; + + tdls_soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (!tdls_soc_obj) { + tdls_err("get soc by vdev failed"); + return QDF_STATUS_E_NOMEM; + } + + tdls_feature_flags = tdls_soc_obj->tdls_configs.tdls_feature_flags; + if (!TDLS_IS_ENABLED(tdls_feature_flags)) { + tdls_debug("disabled in ini"); + return QDF_STATUS_E_NOSUPPORT; + } + + tdls_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_TDLS); + if (!tdls_vdev_obj) { + tdls_err("Failed to get tdls vdev object"); + return QDF_STATUS_E_FAILURE; + } + + status = wlan_objmgr_vdev_component_obj_detach(vdev, + WLAN_UMAC_COMP_TDLS, + tdls_vdev_obj); + 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; +} + +/** + * __tdls_get_all_peers_from_list() - get all the tdls peers from the list + * @get_tdls_peers: get_tdls_peers object + * + * Return: int + */ +static int __tdls_get_all_peers_from_list( + struct tdls_get_all_peers *get_tdls_peers) +{ + int i; + int len, init_len; + qdf_list_t *head; + qdf_list_node_t *p_node; + struct tdls_peer *curr_peer; + char *buf; + int buf_len; + struct tdls_vdev_priv_obj *tdls_vdev; + QDF_STATUS status; + + tdls_notice("Enter "); + + buf = get_tdls_peers->buf; + buf_len = get_tdls_peers->buf_len; + + if (!tdls_is_vdev_connected(get_tdls_peers->vdev)) { + len = qdf_scnprintf(buf, buf_len, + "\nSTA is not associated\n"); + return len; + } + + tdls_vdev = wlan_vdev_get_tdls_vdev_obj(get_tdls_peers->vdev); + + if (!tdls_vdev) { + len = qdf_scnprintf(buf, buf_len, "TDLS not enabled\n"); + return len; + } + + init_len = buf_len; + len = qdf_scnprintf(buf, buf_len, + "\n%-18s%-3s%-4s%-3s%-5s\n", + "MAC", "Id", "cap", "up", "RSSI"); + buf += len; + buf_len -= len; + len = qdf_scnprintf(buf, buf_len, + "---------------------------------\n"); + buf += len; + buf_len -= len; + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &tdls_vdev->peer_list[i]; + status = qdf_list_peek_front(head, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + curr_peer = qdf_container_of(p_node, + struct tdls_peer, node); + if (buf_len < 32 + 1) + break; + len = qdf_scnprintf(buf, buf_len, + QDF_MAC_ADDR_STR "%3d%4s%3s%5d\n", + QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes), + curr_peer->sta_id, + (curr_peer->tdls_support == + TDLS_CAP_SUPPORTED) ? "Y" : "N", + TDLS_IS_LINK_CONNECTED(curr_peer) ? "Y" : + "N", curr_peer->rssi); + buf += len; + buf_len -= len; + status = qdf_list_peek_next(head, p_node, &p_node); + } + } + + tdls_notice("Exit "); + return init_len - buf_len; +} + +/** + * tdls_get_all_peers_from_list() - get all the tdls peers from the list + * @get_tdls_peers: get_tdls_peers object + * + * Return: None + */ +static void tdls_get_all_peers_from_list( + struct tdls_get_all_peers *get_tdls_peers) +{ + int32_t len; + struct tdls_soc_priv_obj *tdls_soc_obj; + struct tdls_osif_indication indication; + + if (!get_tdls_peers->vdev) { + qdf_mem_free(get_tdls_peers); + return; + } + len = __tdls_get_all_peers_from_list(get_tdls_peers); + + indication.status = len; + indication.vdev = get_tdls_peers->vdev; + + tdls_soc_obj = wlan_vdev_get_tdls_soc_obj(get_tdls_peers->vdev); + if (tdls_soc_obj && tdls_soc_obj->tdls_event_cb) + tdls_soc_obj->tdls_event_cb(tdls_soc_obj->tdls_evt_cb_data, + TDLS_EVENT_USER_CMD, &indication); + + qdf_mem_free(get_tdls_peers); +} + +/** + * tdls_process_reset_all_peers() - Reset all tdls peers + * @delete_all_peers_ind: Delete all peers indication + * + * This function is called to reset all tdls peers and + * notify upper layers of teardown inidcation + * + * Return: QDF_STATUS + */ + +static QDF_STATUS tdls_process_reset_all_peers(struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + uint8_t staidx; + struct tdls_peer *curr_peer = NULL; + struct tdls_vdev_priv_obj *tdls_vdev; + struct tdls_soc_priv_obj *tdls_soc; + uint8_t reset_session_id; + + status = tdls_get_vdev_objects(vdev, &tdls_vdev, &tdls_soc); + if (QDF_STATUS_SUCCESS != status) { + tdls_err("tdls objects are NULL "); + return status; + } + + if (!tdls_soc->connected_peer_count) { + tdls_debug("No tdls connected peers"); + return status; + } + + reset_session_id = tdls_vdev->session_id; + for (staidx = 0; staidx < tdls_soc->max_num_tdls_sta; + staidx++) { + if (tdls_soc->tdls_conn_info[staidx].sta_id + == INVALID_TDLS_PEER_ID) + continue; + if (tdls_soc->tdls_conn_info[staidx].session_id != + reset_session_id) + continue; + + curr_peer = + tdls_find_all_peer(tdls_soc, + tdls_soc->tdls_conn_info[staidx]. + peer_mac.bytes); + if (!curr_peer) + continue; + + tdls_notice("indicate TDLS teardown (staId %d)", + curr_peer->sta_id); + + /* Indicate teardown to supplicant */ + tdls_indicate_teardown(tdls_vdev, + curr_peer, + TDLS_TEARDOWN_PEER_UNSPEC_REASON); + + tdls_reset_peer(tdls_vdev, curr_peer->peer_mac.bytes); + + if (tdls_soc->tdls_dereg_peer) + tdls_soc->tdls_dereg_peer( + tdls_soc->tdls_peer_context, + wlan_vdev_get_id(vdev), + curr_peer->sta_id); + tdls_decrement_peer_count(tdls_soc); + tdls_soc->tdls_conn_info[staidx].sta_id = INVALID_TDLS_PEER_ID; + tdls_soc->tdls_conn_info[staidx].session_id = 255; + + qdf_mem_zero(&tdls_soc->tdls_conn_info[staidx].peer_mac, + sizeof(struct qdf_mac_addr)); + } + return status; +} + +/** + * tdls_reset_all_peers() - Reset all tdls peers + * @delete_all_peers_ind: Delete all peers indication + * + * This function is called to reset all tdls peers and + * notify upper layers of teardown inidcation + * + * Return: QDF_STATUS + */ +static QDF_STATUS tdls_reset_all_peers( + struct tdls_delete_all_peers_params *delete_all_peers_ind) +{ + QDF_STATUS status; + + if (!delete_all_peers_ind || !delete_all_peers_ind->vdev) { + tdls_err("invalid param"); + return QDF_STATUS_E_INVAL; + } + + status = tdls_process_reset_all_peers(delete_all_peers_ind->vdev); + + wlan_objmgr_vdev_release_ref(delete_all_peers_ind->vdev, + WLAN_TDLS_SB_ID); + qdf_mem_free(delete_all_peers_ind); + + 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%pK", msg); + QDF_ASSERT(0); + return QDF_STATUS_E_NULL_VALUE; + } + tdls_debug("TDLS process command: %d", msg->type); + + switch (msg->type) { + case TDLS_CMD_TX_ACTION: + tdls_process_mgmt_req(msg->bodyptr); + 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: + tdls_process_enable_link(msg->bodyptr); + break; + case TDLS_CMD_DISABLE_LINK: + tdls_process_del_peer(msg->bodyptr); + break; + case TDLS_CMD_CONFIG_FORCE_PEER: + tdls_process_setup_peer(msg->bodyptr); + break; + case TDLS_CMD_REMOVE_FORCE_PEER: + tdls_process_remove_force_peer(msg->bodyptr); + break; + case TDLS_CMD_STATS_UPDATE: + break; + case TDLS_CMD_CONFIG_UPDATE: + break; + case TDLS_CMD_SET_RESPONDER: + tdls_set_responder(msg->bodyptr); + break; + case TDLS_CMD_SCAN_DONE: + tdls_scan_done_callback(msg->bodyptr); + break; + case TDLS_NOTIFY_STA_CONNECTION: + tdls_notify_sta_connect(msg->bodyptr); + break; + case TDLS_NOTIFY_STA_DISCONNECTION: + tdls_notify_sta_disconnect(msg->bodyptr); + break; + case TDLS_CMD_SET_TDLS_MODE: + tdls_set_operation_mode(msg->bodyptr); + break; + case TDLS_CMD_SESSION_INCREMENT: + case TDLS_CMD_SESSION_DECREMENT: + tdls_process_policy_mgr_notification(msg->bodyptr); + break; + case TDLS_CMD_TEARDOWN_LINKS: + tdls_teardown_connections(msg->bodyptr); + break; + case TDLS_NOTIFY_RESET_ADAPTERS: + tdls_notify_reset_adapter(msg->bodyptr); + break; + case TDLS_CMD_ANTENNA_SWITCH: + tdls_process_antenna_switch(msg->bodyptr); + break; + case TDLS_CMD_GET_ALL_PEERS: + tdls_get_all_peers_from_list(msg->bodyptr); + break; + case TDLS_CMD_SET_OFFCHANNEL: + tdls_process_set_offchannel(msg->bodyptr); + break; + case TDLS_CMD_SET_OFFCHANMODE: + tdls_process_set_offchan_mode(msg->bodyptr); + break; + case TDLS_CMD_SET_SECOFFCHANOFFSET: + tdls_process_set_secoffchanneloffset(msg->bodyptr); + break; + case TDLS_DELETE_ALL_PEERS_INDICATION: + tdls_reset_all_peers(msg->bodyptr); + break; + default: + break; + } + + return status; +} + +QDF_STATUS tdls_process_evt(struct scheduler_msg *msg) +{ + struct wlan_objmgr_vdev *vdev; + struct tdls_event_notify *notify; + struct tdls_event_info *event; + + if (!msg || !msg->bodyptr) { + tdls_err("msg is not valid: %pK", msg); + return QDF_STATUS_E_NULL_VALUE; + } + notify = msg->bodyptr; + vdev = notify->vdev; + if (!vdev) { + tdls_err("NULL vdev object"); + qdf_mem_free(notify); + return QDF_STATUS_E_NULL_VALUE; + } + event = ¬ify->event; + + tdls_debug("evt type: %d", event->message_type); + switch (event->message_type) { + case TDLS_SHOULD_DISCOVER: + tdls_process_should_discover(vdev, event); + break; + case TDLS_SHOULD_TEARDOWN: + case TDLS_PEER_DISCONNECTED: + tdls_process_should_teardown(vdev, event); + break; + case TDLS_CONNECTION_TRACKER_NOTIFY: + tdls_process_connection_tracker_notify(vdev, event); + break; + default: + break; + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); + qdf_mem_free(notify); + + return QDF_STATUS_SUCCESS; +} + +void tdls_timer_restart(struct wlan_objmgr_vdev *vdev, + qdf_mc_timer_t *timer, + uint32_t expiration_time) +{ + qdf_mc_timer_start(timer, expiration_time); +} + +/** + * wlan_hdd_tdls_monitor_timers_stop() - stop all monitoring timers + * @hdd_tdls_ctx: TDLS context + * + * Return: none + */ +static void tdls_monitor_timers_stop(struct tdls_vdev_priv_obj *tdls_vdev) +{ + qdf_mc_timer_stop(&tdls_vdev->peer_discovery_timer); +} + +/** + * tdls_peer_idle_timers_stop() - stop peer idle timers + * @tdls_vdev: TDLS vdev object + * + * Loop through the idle peer list and stop their timers + * + * Return: None + */ +static void tdls_peer_idle_timers_stop(struct tdls_vdev_priv_obj *tdls_vdev) +{ + int i; + qdf_list_t *head; + qdf_list_node_t *p_node; + struct tdls_peer *curr_peer; + QDF_STATUS status; + + tdls_vdev->discovery_peer_cnt = 0; + + for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) { + head = &tdls_vdev->peer_list[i]; + status = qdf_list_peek_front(head, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + curr_peer = qdf_container_of(p_node, struct tdls_peer, + node); + if (curr_peer->is_peer_idle_timer_initialised) + qdf_mc_timer_stop(&curr_peer->peer_idle_timer); + status = qdf_list_peek_next(head, p_node, &p_node); + } + } + +} + +/** + * wlan_hdd_tdls_ct_timers_stop() - stop tdls connection tracker timers + * @tdls_vdev: TDLS vdev + * + * Return: None + */ +static void tdls_ct_timers_stop(struct tdls_vdev_priv_obj *tdls_vdev) +{ + qdf_mc_timer_stop(&tdls_vdev->peer_update_timer); + tdls_peer_idle_timers_stop(tdls_vdev); +} + +/** + * wlan_hdd_tdls_timers_stop() - stop all the tdls timers running + * @tdls_vdev: TDLS vdev + * + * Return: none + */ +void tdls_timers_stop(struct tdls_vdev_priv_obj *tdls_vdev) +{ + tdls_monitor_timers_stop(tdls_vdev); + tdls_ct_timers_stop(tdls_vdev); +} + +QDF_STATUS tdls_get_vdev_objects(struct wlan_objmgr_vdev *vdev, + struct tdls_vdev_priv_obj **tdls_vdev_obj, + struct tdls_soc_priv_obj **tdls_soc_obj) +{ + enum QDF_OPMODE device_mode; + + if (NULL == vdev) + return QDF_STATUS_E_FAILURE; + + *tdls_vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev); + if (NULL == (*tdls_vdev_obj)) + return QDF_STATUS_E_FAILURE; + + *tdls_soc_obj = wlan_vdev_get_tdls_soc_obj(vdev); + if (NULL == (*tdls_soc_obj)) + return QDF_STATUS_E_FAILURE; + + device_mode = wlan_vdev_mlme_get_opmode(vdev); + + if (device_mode != QDF_STA_MODE && + device_mode != QDF_P2P_CLIENT_MODE) + return QDF_STATUS_E_FAILURE; + + return QDF_STATUS_SUCCESS; +} + +/** + * tdls_state_param_setting_dump() - print tdls state & parameters to send to fw + * @info: tdls setting to be sent to fw + * + * Return: void + */ +static void tdls_state_param_setting_dump(struct tdls_info *info) +{ + if (!info) + return; + + tdls_debug("Setting tdls state and param in fw: vdev_id: %d, tdls_state: %d, notification_interval_ms: %d, tx_discovery_threshold: %d, tx_teardown_threshold: %d, rssi_teardown_threshold: %d, rssi_delta: %d, tdls_options: 0x%x, peer_traffic_ind_window: %d, peer_traffic_response_timeout: %d, puapsd_mask: 0x%x, puapsd_inactivity_time: %d, puapsd_rx_frame_threshold: %d, teardown_notification_ms: %d, tdls_peer_kickout_threshold: %d", + info->vdev_id, + info->tdls_state, + info->notification_interval_ms, + info->tx_discovery_threshold, + info->tx_teardown_threshold, + info->rssi_teardown_threshold, + info->rssi_delta, + info->tdls_options, + info->peer_traffic_ind_window, + info->peer_traffic_response_timeout, + info->puapsd_mask, + info->puapsd_inactivity_time, + info->puapsd_rx_frame_threshold, + info->teardown_notification_ms, + info->tdls_peer_kickout_threshold); + +} + +QDF_STATUS tdls_set_offchan_mode(struct wlan_objmgr_psoc *psoc, + struct tdls_channel_switch_params *param) +{ + QDF_STATUS status; + + /* wmi_unified_set_tdls_offchan_mode_cmd() will be called directly */ + status = tgt_tdls_set_offchan_mode(psoc, param); + + if (!QDF_IS_STATUS_SUCCESS(status)) + status = QDF_STATUS_E_FAILURE; + + return status; +} + +/** + * tdls_update_fw_tdls_state() - update tdls status info + * @tdls_soc_obj: TDLS soc object + * @tdls_info_to_fw: TDLS state info to update in f/w. + * + * send message to WMA to set TDLS state in f/w + * + * Return: QDF_STATUS. + */ +static +QDF_STATUS tdls_update_fw_tdls_state(struct tdls_soc_priv_obj *tdls_soc_obj, + struct tdls_info *tdls_info_to_fw) +{ + QDF_STATUS status; + + /* wmi_unified_update_fw_tdls_state_cmd() will be called directly */ + status = tgt_tdls_set_fw_state(tdls_soc_obj->soc, tdls_info_to_fw); + + if (!QDF_IS_STATUS_SUCCESS(status)) + status = QDF_STATUS_E_FAILURE; + + return status; +} + +bool tdls_check_is_tdls_allowed(struct wlan_objmgr_vdev *vdev) +{ + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct tdls_soc_priv_obj *tdls_soc_obj; + bool state = false; + + if (QDF_STATUS_SUCCESS != wlan_objmgr_vdev_try_get_ref(vdev, + WLAN_TDLS_NB_ID)) + return state; + + if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(vdev, &tdls_vdev_obj, + &tdls_soc_obj)) { + wlan_objmgr_vdev_release_ref(vdev, + WLAN_TDLS_NB_ID); + return state; + } + + if (policy_mgr_get_connection_count(tdls_soc_obj->soc) == 1) + state = true; + else + tdls_warn("Concurrent sessions are running or TDLS disabled"); + /* If any concurrency is detected */ + /* print session information */ + wlan_objmgr_vdev_release_ref(vdev, + WLAN_TDLS_NB_ID); + return state; +} + +/** + * cds_set_tdls_ct_mode() - Set the tdls connection tracker mode + * @hdd_ctx: hdd context + * + * This routine is called to set the tdls connection tracker operation status + * + * Return: NONE + */ +void tdls_set_ct_mode(struct wlan_objmgr_psoc *psoc) +{ + bool state = false; + struct tdls_soc_priv_obj *tdls_soc_obj; + + tdls_soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (NULL == tdls_soc_obj) + return; + + /* If any concurrency is detected, skip tdls pkt tracker */ + if (policy_mgr_get_connection_count(psoc) > 1) { + state = false; + goto set_state; + } + + if (TDLS_SUPPORT_DISABLED == tdls_soc_obj->tdls_current_mode || + TDLS_SUPPORT_SUSPENDED == tdls_soc_obj->tdls_current_mode || + !TDLS_IS_IMPLICIT_TRIG_ENABLED( + tdls_soc_obj->tdls_configs.tdls_feature_flags)) { + state = false; + goto set_state; + } else if (policy_mgr_mode_specific_connection_count(psoc, + PM_STA_MODE, + NULL) == 1) { + state = true; + } else if (policy_mgr_mode_specific_connection_count(psoc, + PM_P2P_CLIENT_MODE, + NULL) == 1){ + state = true; + } else { + state = false; + goto set_state; + } + + /* In case of TDLS external control, peer should be added + * by the user space to start connection tracker. + */ + if (TDLS_IS_EXTERNAL_CONTROL_ENABLED( + tdls_soc_obj->tdls_configs.tdls_feature_flags)) { + if (tdls_soc_obj->tdls_external_peer_count) + state = true; + else + state = false; + } + +set_state: + tdls_soc_obj->enable_tdls_connection_tracker = state; + + tdls_debug("enable_tdls_connection_tracker %d", + tdls_soc_obj->enable_tdls_connection_tracker); +} + +QDF_STATUS +tdls_process_policy_mgr_notification(struct wlan_objmgr_psoc *psoc) +{ + if (!psoc) { + tdls_err("psoc: %pK", psoc); + return QDF_STATUS_E_NULL_VALUE; + } + tdls_debug("enter "); + tdls_set_ct_mode(psoc); + tdls_debug("exit "); + return QDF_STATUS_SUCCESS; +} + +/** + * tdls_get_vdev() - Get tdls specific vdev object manager + * @psoc: wlan psoc object manager + * @dbg_id: debug id + * + * If TDLS possible, return the corresponding vdev + * to enable TDLS in the system. + * + * Return: vdev manager pointer or NULL. + */ +struct wlan_objmgr_vdev *tdls_get_vdev(struct wlan_objmgr_psoc *psoc, + wlan_objmgr_ref_dbgid dbg_id) +{ + uint32_t vdev_id; + + if (policy_mgr_get_connection_count(psoc) > 1) + return NULL; + + vdev_id = policy_mgr_mode_specific_vdev_id(psoc, PM_STA_MODE); + + if (WLAN_INVALID_VDEV_ID != vdev_id) + return wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + vdev_id, + dbg_id); + + vdev_id = policy_mgr_mode_specific_vdev_id(psoc, PM_P2P_CLIENT_MODE); + + if (WLAN_INVALID_VDEV_ID != vdev_id) + return wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + vdev_id, + dbg_id); + + return NULL; +} + +static QDF_STATUS tdls_post_msg_flush_cb(struct scheduler_msg *msg) +{ + void *ptr = msg->bodyptr; + struct wlan_objmgr_vdev *vdev = NULL; + + switch (msg->type) { + case TDLS_NOTIFY_STA_DISCONNECTION: + vdev = ((struct tdls_sta_notify_params *)ptr)->vdev; + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(ptr); + break; + + case TDLS_DELETE_ALL_PEERS_INDICATION: + vdev = ((struct tdls_delete_all_peers_params *)ptr)->vdev; + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); + qdf_mem_free(ptr); + break; + + case TDLS_CMD_SCAN_DONE: + case TDLS_CMD_SESSION_INCREMENT: + case TDLS_CMD_SESSION_DECREMENT: + break; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * tdls_process_session_update() - update session count information + * @psoc: soc object + * @notification: TDLS os if notification + * + * update the session information in connection tracker + * + * Return: None + */ +static void tdls_process_session_update(struct wlan_objmgr_psoc *psoc, + enum tdls_command_type cmd_type) +{ + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + msg.bodyptr = psoc; + msg.callback = tdls_process_cmd; + msg.flush_callback = tdls_post_msg_flush_cb; + msg.type = (uint16_t)cmd_type; + + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) + tdls_alert("message post failed "); +} + +void tdls_notify_increment_session(struct wlan_objmgr_psoc *psoc) +{ + tdls_process_session_update(psoc, TDLS_CMD_SESSION_INCREMENT); +} + +void tdls_notify_decrement_session(struct wlan_objmgr_psoc *psoc) +{ + tdls_process_session_update(psoc, TDLS_CMD_SESSION_DECREMENT); +} + +void tdls_send_update_to_fw(struct tdls_vdev_priv_obj *tdls_vdev_obj, + struct tdls_soc_priv_obj *tdls_soc_obj, + bool tdls_prohibited, + bool tdls_chan_swit_prohibited, + bool sta_connect_event, + uint8_t session_id) +{ + struct tdls_info *tdls_info_to_fw; + struct tdls_config_params *threshold_params; + uint32_t tdls_feature_flags; + QDF_STATUS status; + + tdls_feature_flags = tdls_soc_obj->tdls_configs.tdls_feature_flags; + if (!TDLS_IS_ENABLED(tdls_feature_flags)) { + tdls_debug("TDLS mode is not enabled"); + return; + } + + if (tdls_soc_obj->set_state_info.set_state_cnt == 0 && + !sta_connect_event) { + return; + } + + /* If AP or caller indicated TDLS Prohibited then disable tdls mode */ + if (sta_connect_event) { + if (tdls_prohibited) { + tdls_soc_obj->tdls_current_mode = + TDLS_SUPPORT_DISABLED; + } else { + tdls_debug("TDLS feature flags from ini %d ", + tdls_feature_flags); + if (!TDLS_IS_IMPLICIT_TRIG_ENABLED(tdls_feature_flags)) + tdls_soc_obj->tdls_current_mode = + TDLS_SUPPORT_EXP_TRIG_ONLY; + else if (TDLS_IS_EXTERNAL_CONTROL_ENABLED( + tdls_feature_flags)) + tdls_soc_obj->tdls_current_mode = + TDLS_SUPPORT_EXT_CONTROL; + else + tdls_soc_obj->tdls_current_mode = + TDLS_SUPPORT_IMP_MODE; + } + } else { + tdls_soc_obj->tdls_current_mode = + TDLS_SUPPORT_DISABLED; + } + + tdls_info_to_fw = qdf_mem_malloc(sizeof(struct tdls_info)); + + if (!tdls_info_to_fw) { + tdls_err("memory allocation failed for tdlsParams"); + QDF_ASSERT(0); + return; + } + + threshold_params = &tdls_vdev_obj->threshold_config; + + tdls_info_to_fw->notification_interval_ms = + threshold_params->tx_period_t; + tdls_info_to_fw->tx_discovery_threshold = + threshold_params->tx_packet_n; + tdls_info_to_fw->tx_teardown_threshold = + threshold_params->idle_packet_n; + tdls_info_to_fw->rssi_teardown_threshold = + threshold_params->rssi_teardown_threshold; + tdls_info_to_fw->rssi_delta = threshold_params->rssi_delta; + + if (tdls_soc_obj->set_state_info.set_state_cnt == 1 && + sta_connect_event) { + tdls_warn("Concurrency not allowed in TDLS! set state cnt %d", + tdls_soc_obj->set_state_info.set_state_cnt); + /* disable off channel and teardown links */ + /* Go through the peer list and delete them */ + tdls_disable_offchan_and_teardown_links(tdls_vdev_obj->vdev); + tdls_soc_obj->tdls_current_mode = TDLS_SUPPORT_DISABLED; + tdls_info_to_fw->vdev_id = tdls_soc_obj->set_state_info.vdev_id; + } else { + tdls_info_to_fw->vdev_id = session_id; + } + + /* record the session id in vdev context */ + tdls_vdev_obj->session_id = session_id; + tdls_info_to_fw->tdls_state = tdls_soc_obj->tdls_current_mode; + tdls_info_to_fw->tdls_options = 0; + + /* Do not enable TDLS offchannel, if AP prohibited TDLS + * channel switch + */ + if (TDLS_IS_OFF_CHANNEL_ENABLED(tdls_feature_flags) && + (!tdls_chan_swit_prohibited)) + tdls_info_to_fw->tdls_options = ENA_TDLS_OFFCHAN; + + if (TDLS_IS_BUFFER_STA_ENABLED(tdls_feature_flags)) + tdls_info_to_fw->tdls_options |= ENA_TDLS_BUFFER_STA; + if (TDLS_IS_SLEEP_STA_ENABLED(tdls_feature_flags)) + tdls_info_to_fw->tdls_options |= ENA_TDLS_SLEEP_STA; + + + tdls_info_to_fw->peer_traffic_ind_window = + tdls_soc_obj->tdls_configs.tdls_uapsd_pti_window; + tdls_info_to_fw->peer_traffic_response_timeout = + tdls_soc_obj->tdls_configs.tdls_uapsd_ptr_timeout; + tdls_info_to_fw->puapsd_mask = + tdls_soc_obj->tdls_configs.tdls_uapsd_mask; + tdls_info_to_fw->puapsd_inactivity_time = + tdls_soc_obj->tdls_configs.tdls_uapsd_inactivity_time; + tdls_info_to_fw->puapsd_rx_frame_threshold = + tdls_soc_obj->tdls_configs.tdls_rx_pkt_threshold; + tdls_info_to_fw->teardown_notification_ms = + tdls_soc_obj->tdls_configs.tdls_idle_timeout; + tdls_info_to_fw->tdls_peer_kickout_threshold = + tdls_soc_obj->tdls_configs.tdls_peer_kickout_threshold; + + tdls_state_param_setting_dump(tdls_info_to_fw); + + status = tdls_update_fw_tdls_state(tdls_soc_obj, tdls_info_to_fw); + if (QDF_STATUS_SUCCESS != status) + goto done; + + if (sta_connect_event) { + tdls_soc_obj->set_state_info.set_state_cnt++; + tdls_soc_obj->set_state_info.vdev_id = session_id; + } else { + tdls_soc_obj->set_state_info.set_state_cnt--; + } + + tdls_debug("TDLS Set state cnt %d", + tdls_soc_obj->set_state_info.set_state_cnt); +done: + qdf_mem_free(tdls_info_to_fw); + return; +} + +static QDF_STATUS +tdls_process_sta_connect(struct tdls_sta_notify_params *notify) +{ + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct tdls_soc_priv_obj *tdls_soc_obj; + + if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(notify->vdev, + &tdls_vdev_obj, + &tdls_soc_obj)) + return QDF_STATUS_E_INVAL; + + + tdls_debug("Check and update TDLS state"); + + if (policy_mgr_get_connection_count(tdls_soc_obj->soc) > 1) { + tdls_debug("Concurrent sessions exist, TDLS can't be enabled"); + return QDF_STATUS_SUCCESS; + } + + /* Association event */ + if (!tdls_soc_obj->tdls_disable_in_progress) { + tdls_send_update_to_fw(tdls_vdev_obj, + tdls_soc_obj, + notify->tdls_prohibited, + notify->tdls_chan_swit_prohibited, + true, + notify->session_id); + } + + /* check and set the connection tracker */ + tdls_set_ct_mode(tdls_soc_obj->soc); + if (tdls_soc_obj->enable_tdls_connection_tracker) + tdls_implicit_enable(tdls_vdev_obj); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_notify_sta_connect(struct tdls_sta_notify_params *notify) +{ + QDF_STATUS status; + + if (!notify || !notify->vdev) { + tdls_err("invalid param"); + return QDF_STATUS_E_INVAL; + } + + status = tdls_process_sta_connect(notify); + + wlan_objmgr_vdev_release_ref(notify->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(notify); + + return status; +} + +static QDF_STATUS +tdls_process_sta_disconnect(struct tdls_sta_notify_params *notify) +{ + struct tdls_vdev_priv_obj *tdls_vdev_obj; + struct tdls_vdev_priv_obj *curr_tdls_vdev; + struct tdls_soc_priv_obj *tdls_soc_obj; + struct tdls_soc_priv_obj *curr_tdls_soc; + struct wlan_objmgr_vdev *temp_vdev = NULL; + + + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(notify->vdev, + &tdls_vdev_obj, + &tdls_soc_obj)) + return QDF_STATUS_E_INVAL; + + /* if the disconnect comes from user space, we have to delete all the + * tdls peers before sending the set state cmd. + */ + if (notify->user_disconnect) + return tdls_delete_all_tdls_peers(notify->vdev, tdls_soc_obj); + + tdls_debug("Check and update TDLS state"); + + curr_tdls_vdev = tdls_vdev_obj; + curr_tdls_soc = tdls_soc_obj; + + /* Disassociation event */ + if (!tdls_soc_obj->tdls_disable_in_progress) + tdls_send_update_to_fw(tdls_vdev_obj, tdls_soc_obj, false, + false, false, notify->session_id); + + /* If concurrency is not marked, then we have to + * check, whether TDLS could be enabled in the + * system after this disassoc event. + */ + if (!notify->lfr_roam && !tdls_soc_obj->tdls_disable_in_progress) { + temp_vdev = tdls_get_vdev(tdls_soc_obj->soc, WLAN_TDLS_NB_ID); + if (NULL != temp_vdev) { + status = tdls_get_vdev_objects(temp_vdev, + &tdls_vdev_obj, + &tdls_soc_obj); + if (QDF_STATUS_SUCCESS == status) { + tdls_send_update_to_fw(tdls_vdev_obj, + tdls_soc_obj, + false, + false, + true, + notify->session_id); + curr_tdls_vdev = tdls_vdev_obj; + curr_tdls_soc = tdls_soc_obj; + } + } + } + + /* Check and set the connection tracker and implicit timers */ + tdls_set_ct_mode(curr_tdls_soc->soc); + if (curr_tdls_soc->enable_tdls_connection_tracker) + tdls_implicit_enable(curr_tdls_vdev); + else + tdls_implicit_disable(curr_tdls_vdev); + + /* release the vdev ref , if temp vdev was acquired */ + if (temp_vdev) + wlan_objmgr_vdev_release_ref(temp_vdev, + WLAN_TDLS_NB_ID); + + return status; +} + +QDF_STATUS tdls_notify_sta_disconnect(struct tdls_sta_notify_params *notify) +{ + QDF_STATUS status; + + if (!notify || !notify->vdev) { + tdls_err("invalid param"); + return QDF_STATUS_E_INVAL; + } + + status = tdls_process_sta_disconnect(notify); + + wlan_objmgr_vdev_release_ref(notify->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(notify); + + return status; +} + +static void tdls_process_reset_adapter(struct wlan_objmgr_vdev *vdev) +{ + struct tdls_vdev_priv_obj *tdls_vdev; + + tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev); + if (!tdls_vdev) + return; + tdls_timers_stop(tdls_vdev); +} + +void tdls_notify_reset_adapter(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + QDF_ASSERT(0); + return; + } + + if (QDF_STATUS_SUCCESS != wlan_objmgr_vdev_try_get_ref(vdev, + WLAN_TDLS_NB_ID)) + return; + + tdls_process_reset_adapter(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); +} + +QDF_STATUS tdls_peers_deleted_notification(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id) +{ + struct scheduler_msg msg = {0, }; + struct tdls_sta_notify_params *notify; + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + + notify = qdf_mem_malloc(sizeof(*notify)); + if (!notify) { + tdls_err("memory allocation failed !!!"); + return QDF_STATUS_E_NULL_VALUE; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + vdev_id, + WLAN_TDLS_NB_ID); + + if (!vdev) { + tdls_err("vdev not exist for the vdev id %d", + vdev_id); + qdf_mem_free(notify); + return QDF_STATUS_E_INVAL; + } + + notify->lfr_roam = true; + notify->tdls_chan_swit_prohibited = false; + notify->tdls_prohibited = false; + notify->session_id = vdev_id; + notify->vdev = vdev; + notify->user_disconnect = false; + + msg.bodyptr = notify; + msg.callback = tdls_process_cmd; + msg.flush_callback = tdls_post_msg_flush_cb; + msg.type = TDLS_NOTIFY_STA_DISCONNECTION; + + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(notify); + tdls_alert("message post failed "); + + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_delete_all_peers_indication(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id) +{ + struct scheduler_msg msg = {0, }; + struct tdls_delete_all_peers_params *indication; + QDF_STATUS status; + struct wlan_objmgr_vdev *vdev; + + indication = qdf_mem_malloc(sizeof(*indication)); + if (!indication) { + tdls_err("memory allocation failed !!!"); + return QDF_STATUS_E_NULL_VALUE; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + vdev_id, + WLAN_TDLS_SB_ID); + + if (!vdev) { + tdls_err("vdev not exist for the session id %d", + vdev_id); + qdf_mem_free(indication); + return QDF_STATUS_E_INVAL; + } + + indication->vdev = vdev; + + msg.bodyptr = indication; + msg.callback = tdls_process_cmd; + msg.type = TDLS_DELETE_ALL_PEERS_INDICATION; + msg.flush_callback = tdls_post_msg_flush_cb; + + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); + qdf_mem_free(indication); + tdls_alert("message post failed "); + return QDF_STATUS_E_FAILURE; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * tdls_set_mode_in_vdev() - set TDLS mode + * @tdls_vdev: tdls vdev object + * @tdls_soc: tdls soc object + * @tdls_mode: TDLS mode + * @source: TDLS disable source enum values + * + * Return: Void + */ +static void tdls_set_mode_in_vdev(struct tdls_vdev_priv_obj *tdls_vdev, + struct tdls_soc_priv_obj *tdls_soc, + enum tdls_feature_mode tdls_mode, + enum tdls_disable_sources source) +{ + if (!tdls_vdev) + return; + tdls_debug("enter tdls mode is %d", tdls_mode); + + if (TDLS_SUPPORT_IMP_MODE == tdls_mode || + TDLS_SUPPORT_EXT_CONTROL == tdls_mode) { + clear_bit((unsigned long)source, + &tdls_soc->tdls_source_bitmap); + /* + * Check if any TDLS source bit is set and if + * bitmap is not zero then we should not + * enable TDLS + */ + if (tdls_soc->tdls_source_bitmap) { + tdls_notice("Don't enable TDLS, source bitmap: %lu", + tdls_soc->tdls_source_bitmap); + return; + } + tdls_implicit_enable(tdls_vdev); + /* tdls implicit mode is enabled, so + * enable the connection tracker + */ + tdls_soc->enable_tdls_connection_tracker = + true; + } else if (TDLS_SUPPORT_DISABLED == tdls_mode) { + set_bit((unsigned long)source, + &tdls_soc->tdls_source_bitmap); + tdls_implicit_disable(tdls_vdev); + /* If tdls implicit mode is disabled, then + * stop the connection tracker. + */ + tdls_soc->enable_tdls_connection_tracker = + false; + } else if (TDLS_SUPPORT_EXP_TRIG_ONLY == + tdls_mode) { + clear_bit((unsigned long)source, + &tdls_soc->tdls_source_bitmap); + tdls_implicit_disable(tdls_vdev); + /* If tdls implicit mode is disabled, then + * stop the connection tracker. + */ + tdls_soc->enable_tdls_connection_tracker = + false; + + /* + * Check if any TDLS source bit is set and if + * bitmap is not zero then we should not + * enable TDLS + */ + if (tdls_soc->tdls_source_bitmap) + return; + } + tdls_debug("exit "); + +} + +/** + * tdls_set_current_mode() - set TDLS mode + * @tdls_soc: tdls soc object + * @tdls_mode: TDLS mode + * @update_last: indicate to record the last tdls mode + * @source: TDLS disable source enum values + * + * Return: Void + */ +static void tdls_set_current_mode(struct tdls_soc_priv_obj *tdls_soc, + enum tdls_feature_mode tdls_mode, + bool update_last, + enum tdls_disable_sources source) +{ + struct wlan_objmgr_vdev *vdev; + struct tdls_vdev_priv_obj *tdls_vdev; + + if (!tdls_soc) + return; + + tdls_debug("mode %d", (int)tdls_mode); + + if (update_last) + tdls_soc->tdls_last_mode = tdls_mode; + + if (tdls_soc->tdls_current_mode == tdls_mode) { + tdls_debug("already in mode %d", tdls_mode); + + switch (tdls_mode) { + /* TDLS is already enabled hence clear source mask, return */ + case TDLS_SUPPORT_IMP_MODE: + case TDLS_SUPPORT_EXP_TRIG_ONLY: + case TDLS_SUPPORT_EXT_CONTROL: + clear_bit((unsigned long)source, + &tdls_soc->tdls_source_bitmap); + tdls_debug("clear source mask:%d", source); + return; + /* TDLS is already disabled hence set source mask, return */ + case TDLS_SUPPORT_DISABLED: + set_bit((unsigned long)source, + &tdls_soc->tdls_source_bitmap); + tdls_debug("set source mask:%d", source); + return; + default: + return; + } + } + + /* get sta vdev */ + vdev = wlan_objmgr_get_vdev_by_opmode_from_psoc(tdls_soc->soc, + QDF_STA_MODE, + WLAN_TDLS_NB_ID); + if (NULL != vdev) { + tdls_debug("set mode in tdls vdev "); + tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev); + if (!tdls_vdev) + tdls_set_mode_in_vdev(tdls_vdev, tdls_soc, + tdls_mode, source); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + } + + /* get p2p client vdev */ + vdev = wlan_objmgr_get_vdev_by_opmode_from_psoc(tdls_soc->soc, + QDF_P2P_CLIENT_MODE, + WLAN_TDLS_NB_ID); + if (NULL != vdev) { + tdls_debug("set mode in tdls vdev "); + tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev); + if (!tdls_vdev) + tdls_set_mode_in_vdev(tdls_vdev, tdls_soc, + tdls_mode, source); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + } + + if (!update_last) + tdls_soc->tdls_last_mode = tdls_soc->tdls_current_mode; + + tdls_soc->tdls_current_mode = tdls_mode; + +} + +QDF_STATUS tdls_set_operation_mode(struct tdls_set_mode_params *tdls_set_mode) +{ + struct tdls_soc_priv_obj *tdls_soc; + struct tdls_vdev_priv_obj *tdls_vdev; + QDF_STATUS status; + + if (!tdls_set_mode || !tdls_set_mode->vdev) + return QDF_STATUS_E_INVAL; + + status = tdls_get_vdev_objects(tdls_set_mode->vdev, + &tdls_vdev, &tdls_soc); + + if (QDF_IS_STATUS_ERROR(status)) + goto release_mode_ref; + + tdls_set_current_mode(tdls_soc, + tdls_set_mode->tdls_mode, + tdls_set_mode->update_last, + tdls_set_mode->source); + +release_mode_ref: + wlan_objmgr_vdev_release_ref(tdls_set_mode->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(tdls_set_mode); + return status; +} + +/** + * wlan_hdd_tdls_scan_done_callback() - callback for tdls scan done event + * @pAdapter: HDD adapter + * + * Return: Void + */ +void tdls_scan_done_callback(struct tdls_soc_priv_obj *tdls_soc) +{ + if (!tdls_soc) + return; + + if (TDLS_SUPPORT_DISABLED == tdls_soc->tdls_current_mode) { + tdls_debug("TDLS mode is disabled OR not enabled"); + return; + } + + /* if tdls was enabled before scan, re-enable tdls mode */ + if (TDLS_SUPPORT_IMP_MODE == tdls_soc->tdls_last_mode || + TDLS_SUPPORT_EXT_CONTROL == tdls_soc->tdls_last_mode || + TDLS_SUPPORT_EXP_TRIG_ONLY == tdls_soc->tdls_last_mode) { + tdls_debug("revert tdls mode %d", + tdls_soc->tdls_last_mode); + + tdls_set_current_mode(tdls_soc, tdls_soc->tdls_last_mode, + false, + TDLS_SET_MODE_SOURCE_SCAN); + } +} + +/** + * tdls_post_scan_done_msg() - post scan done message to tdls cmd queue + * @tdls_soc: tdls soc object + * + * Return: QDF_STATUS_SUCCESS or QDF_STATUS_E_NULL_VALUE + */ +static QDF_STATUS tdls_post_scan_done_msg(struct tdls_soc_priv_obj *tdls_soc) +{ + struct scheduler_msg msg = {0, }; + + if (!tdls_soc) { + tdls_err("tdls_soc: %pK ", tdls_soc); + return QDF_STATUS_E_NULL_VALUE; + } + + msg.bodyptr = tdls_soc; + msg.callback = tdls_process_cmd; + msg.flush_callback = tdls_post_msg_flush_cb; + msg.type = TDLS_CMD_SCAN_DONE; + + return scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); +} + +void tdls_scan_complete_event_handler(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, + void *arg) +{ + enum QDF_OPMODE device_mode; + struct tdls_soc_priv_obj *tdls_soc; + + if (!vdev || !event || !arg) + return; + + if (SCAN_EVENT_TYPE_COMPLETED != event->type) + return; + + device_mode = wlan_vdev_mlme_get_opmode(vdev); + + if (device_mode != QDF_STA_MODE && + device_mode != QDF_P2P_CLIENT_MODE) + return; + tdls_soc = (struct tdls_soc_priv_obj *) arg; + tdls_post_scan_done_msg(tdls_soc); +} + +QDF_STATUS tdls_scan_callback(struct tdls_soc_priv_obj *tdls_soc) +{ + struct tdls_peer *curr_peer; + struct tdls_vdev_priv_obj *tdls_vdev; + struct wlan_objmgr_vdev *vdev; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + /* if tdls is not enabled, then continue scan */ + if (TDLS_SUPPORT_DISABLED == tdls_soc->tdls_current_mode) + return status; + + /* Get the vdev based on vdev operating mode*/ + vdev = tdls_get_vdev(tdls_soc->soc, WLAN_TDLS_NB_ID); + if (!vdev) + return status; + + tdls_vdev = wlan_vdev_get_tdls_vdev_obj(vdev); + if (!tdls_vdev) + goto return_success; + + curr_peer = tdls_is_progress(tdls_vdev, NULL, 0); + if (NULL != curr_peer) { + if (tdls_soc->scan_reject_count++ >= TDLS_SCAN_REJECT_MAX) { + tdls_notice(QDF_MAC_ADDR_STR + ". scan rejected %d. force it to idle", + QDF_MAC_ADDR_ARRAY( + curr_peer->peer_mac.bytes), + tdls_soc->scan_reject_count); + tdls_soc->scan_reject_count = 0; + + tdls_set_peer_link_status(curr_peer, + TDLS_LINK_IDLE, + TDLS_LINK_UNSPECIFIED); + status = QDF_STATUS_SUCCESS; + } else { + tdls_warn("tdls in progress. scan rejected %d", + tdls_soc->scan_reject_count); + status = QDF_STATUS_E_BUSY; + } + } +return_success: + wlan_objmgr_vdev_release_ref(vdev, + WLAN_TDLS_NB_ID); + return status; +} + +void tdls_scan_serialization_comp_info_cb(struct wlan_objmgr_vdev *vdev, + union wlan_serialization_rules_info *comp_info) +{ + struct tdls_soc_priv_obj *tdls_soc; + QDF_STATUS status; + if (!comp_info) + return; + + tdls_soc = tdls_soc_global; + comp_info->scan_info.is_tdls_in_progress = false; + status = tdls_scan_callback(tdls_soc); + if (QDF_STATUS_E_BUSY == status) + comp_info->scan_info.is_tdls_in_progress = true; +} + + diff --git a/components/tdls/core/src/wlan_tdls_main.h b/components/tdls/core/src/wlan_tdls_main.h new file mode 100644 index 000000000000..c03eb16abf36 --- /dev/null +++ b/components/tdls/core/src/wlan_tdls_main.h @@ -0,0 +1,741 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_main.h + * + * TDLS core function declaration + */ + +#if !defined(_WLAN_TDLS_MAIN_H_) +#define _WLAN_TDLS_MAIN_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_serialization_api.h" + +/* Bit mask flag for tdls_option to FW */ +#define ENA_TDLS_OFFCHAN (1 << 0) /* TDLS Off Channel support */ +#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 +/* + * Before UpdateTimer expires, we want to timeout discovery response + * should not be more than 2000. + */ +#define TDLS_DISCOVERY_TIMEOUT_BEFORE_UPDATE 1000 +#define TDLS_SCAN_REJECT_MAX 5 + +#define tdls_debug(params...) \ + QDF_TRACE_DEBUG(QDF_MODULE_ID_TDLS, params) +#define tdls_notice(params...) \ + QDF_TRACE_INFO(QDF_MODULE_ID_TDLS, params) +#define tdls_warn(params...) \ + QDF_TRACE_WARN(QDF_MODULE_ID_TDLS, params) +#define tdls_err(params...) \ + QDF_TRACE_ERROR(QDF_MODULE_ID_TDLS, params) +#define tdls_alert(params...) \ + QDF_TRACE_FATAL(QDF_MODULE_ID_TDLS, params) + +#define tdls_nofl_debug(params...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_TDLS, params) +#define tdls_nofl_notice(params...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_TDLS, params) +#define tdls_nofl_warn(params...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_TDLS, params) +#define tdls_nofl_err(params...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_TDLS, params) +#define tdls_nofl_alert(params...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_TDLS, params) + +#define TDLS_IS_LINK_CONNECTED(peer) \ + ((TDLS_LINK_CONNECTED == (peer)->link_status) || \ + (TDLS_LINK_TEARING == (peer)->link_status)) + +#define SET_BIT(value, mask) ((value) |= (1 << (mask))) +#define CLEAR_BIT(value, mask) ((value) &= ~(1 << (mask))) +#define CHECK_BIT(value, mask) ((value) & (1 << (mask))) +/** + * struct tdls_conn_info - TDLS connection record + * @session_id: session id + * @sta_id: sta id + * @peer_mac: peer address + */ +struct tdls_conn_info { + uint8_t session_id; + uint8_t sta_id; + struct qdf_mac_addr peer_mac; +}; + +/** + * enum tdls_nss_transition_state - TDLS NSS transition states + * @TDLS_NSS_TRANSITION_UNKNOWN: default state + * @TDLS_NSS_TRANSITION_2x2_to_1x1: transition from 2x2 to 1x1 stream + * @TDLS_NSS_TRANSITION_1x1_to_2x2: transition from 1x1 to 2x2 stream + */ +enum tdls_nss_transition_state { + TDLS_NSS_TRANSITION_S_UNKNOWN = 0, + TDLS_NSS_TRANSITION_S_2x2_to_1x1, + TDLS_NSS_TRANSITION_S_1x1_to_2x2, +}; + +/** + * struct tdls_conn_tracker_mac_table - connection tracker peer table + * @mac_address: peer mac address + * @tx_packet_cnt: number of tx pkts + * @rx_packet_cnt: number of rx pkts + * @peer_timestamp_ms: time stamp of latest peer traffic + */ +struct tdls_conn_tracker_mac_table { + struct qdf_mac_addr mac_address; + uint32_t tx_packet_cnt; + uint32_t rx_packet_cnt; + uint32_t peer_timestamp_ms; +}; + +/** + * struct tdls_ct_idle_peer_data - connection tracker idle peer info + * @vdev: vdev object + * @tdls_info: tdls connection info + */ +struct tdls_ct_idle_peer_data { + struct wlan_objmgr_vdev *vdev; + struct tdls_conn_info *tdls_info; +}; + +/** + * struct tdls_set_state_db - to record set tdls state command, we need to + * set correct tdls state to firmware: + * 1. enable tdls in firmware before tdls connection; + * 2. disable tdls if concurrency happen, before disable tdls, all active peer + * should be deleted in firmware. + * + * @set_state_cnt: tdls set state count + * @vdev_id: vdev id of last set state command + */ +struct tdls_set_state_info { + uint8_t set_state_cnt; + uint8_t vdev_id; +}; + +/** + * struct tdls_psoc_priv_ctx - tdls context + * @soc: objmgr psoc + * @tdls_current_mode: current tdls mode + * @tdls_last_mode: last tdls mode + * @scan_reject_count: number of times scan rejected due to TDLS + * @tdls_source_bitmap: bit map to set/reset TDLS by different sources + * @tdls_conn_info: this tdls_conn_info can be removed and we can use peer type + * of peer object to get the active tdls peers + * @tdls_configs: tdls user configure + * @max_num_tdls_sta: maximum TDLS station number allowed upon runtime condition + * @connected_peer_count: tdls peer connected count + * @tdls_off_channel: tdls off channel number + * @tdls_channel_offset: tdls channel offset + * @tdls_fw_off_chan_mode: tdls fw off channel mode + * @enable_tdls_connection_tracker: enable tdls connection tracker + * @tdls_external_peer_count: external tdls peer count + * @tdls_nss_switch_in_progress: tdls antenna switch in progress + * @tdls_nss_teardown_complete: tdls tear down complete + * @tdls_nss_transition_mode: tdls nss transition mode + * @tdls_teardown_peers_cnt: tdls tear down peer count + * @set_state_info: set tdls state info + * @tdls_event_cb: tdls event callback + * @tdls_evt_cb_data: tdls event user data + * @tdls_peer_context: userdata for register/deregister TDLS peer + * @tdls_reg_peer: register tdls peer with datapath + * @tdls_dereg_peer: deregister tdls peer from datapath + * @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 + * @tdls_del_all_peers:store eWNI_SME_DEL_ALL_TDLS_PEERS + * @tdls_update_dp_vdev_flags store CDP_UPDATE_TDLS_FLAGS + * @tdls_idle_peer_data: provide information about idle peer + * @tdls_ct_spinlock: connection tracker spin lock + */ +struct tdls_soc_priv_obj { + struct wlan_objmgr_psoc *soc; + enum tdls_feature_mode tdls_current_mode; + enum tdls_feature_mode tdls_last_mode; + int scan_reject_count; + unsigned long tdls_source_bitmap; + struct tdls_conn_info tdls_conn_info[WLAN_TDLS_STA_MAX_NUM]; + struct tdls_user_config tdls_configs; + uint16_t max_num_tdls_sta; + uint16_t connected_peer_count; + uint8_t tdls_off_channel; + uint16_t tdls_channel_offset; + int32_t tdls_fw_off_chan_mode; + bool enable_tdls_connection_tracker; + uint8_t tdls_external_peer_count; + bool tdls_nss_switch_in_progress; + bool tdls_nss_teardown_complete; + bool tdls_disable_in_progress; + enum tdls_nss_transition_state tdls_nss_transition_mode; + int32_t tdls_teardown_peers_cnt; + struct tdls_set_state_info set_state_info; + tdls_rx_callback tdls_rx_cb; + void *tdls_rx_cb_data; + tdls_wmm_check tdls_wmm_cb; + void *tdls_wmm_cb_data; + tdls_evt_callback tdls_event_cb; + void *tdls_evt_cb_data; + void *tdls_peer_context; + tdls_register_peer_callback tdls_reg_peer; + tdls_deregister_peer_callback tdls_dereg_peer; + tdls_dp_vdev_update_flags_callback tdls_dp_vdev_update; + 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; + uint16_t tdls_del_all_peers; + uint32_t tdls_update_dp_vdev_flags; + struct tdls_ct_idle_peer_data tdls_idle_peer_data; + qdf_spinlock_t tdls_ct_spinlock; +}; + +/** + * struct tdls_vdev_priv_obj - tdls private vdev object + * @vdev: vdev objmgr object + * @peer_list: tdls peer list on this vdev + * @peer_update_timer: connection tracker timer + * @peer_dicovery_timer: peer discovery timer + * @threshold_config: threshold config + * @discovery_peer_cnt: discovery peer count + * @discovery_sent_cnt: discovery sent count + * @ap_rssi: ap rssi + * @curr_candidate: current candidate + * @ct_peer_table: linear mac address table for counting the packets + * @valid_mac_entries: number of valid mac entry in @ct_peer_mac_table + * @magic: magic + * @tx_queue: tx frame queue + */ +struct tdls_vdev_priv_obj { + struct wlan_objmgr_vdev *vdev; + qdf_list_t peer_list[WLAN_TDLS_PEER_LIST_SIZE]; + qdf_mc_timer_t peer_update_timer; + qdf_mc_timer_t peer_discovery_timer; + struct tdls_config_params threshold_config; + int32_t discovery_peer_cnt; + uint32_t discovery_sent_cnt; + int8_t ap_rssi; + struct tdls_peer *curr_candidate; + struct tdls_conn_tracker_mac_table + ct_peer_table[WLAN_TDLS_CT_TABLE_SIZE]; + uint8_t valid_mac_entries; + uint32_t magic; + uint8_t session_id; + 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 + * @vdev_priv: tdls vdev priv obj + * @peer_mac: peer mac address + * @sta_id: station identifier + * @rssi: rssi + * @tdls_support: tdls support + * @link_status: tdls link status + * @is_responder: is responder + * @discovery_processed: dicovery processed + * @discovery_attempt: discovery attempt + * @tx_pkt: tx packet + * @rx_pkt: rx packet + * @uapsd_queues: uapsd queues + * @max_sp: max sp + * @buf_sta_capable: is buffer sta + * @off_channel_capable: is offchannel supported flag + * @supported_channels_len: supported channels length + * @supported_channels: supported channels + * @supported_oper_classes_len: supported operation classes length + * @supported_oper_classes: supported operation classes + * @is_forced_peer: is forced peer + * @op_class_for_pref_off_chan: op class for preferred off channel + * @pref_off_chan_num: preferred off channel number + * @op_class_for_pref_off_chan_is_set: op class for preferred off channel set + * @peer_idle_timer: time to check idle traffic in tdls peers + * @is_peer_idle_timer_initialised: Flag to check idle timer init + * @spatial_streams: Number of TX/RX spatial streams for TDLS + * @reason: reason + * @state_change_notification: state change notification + * @qos: QOS capability of TDLS link + */ +struct tdls_peer { + qdf_list_node_t node; + struct tdls_vdev_priv_obj *vdev_priv; + struct qdf_mac_addr peer_mac; + uint16_t sta_id; + int8_t rssi; + enum tdls_peer_capab tdls_support; + enum tdls_link_state link_status; + uint8_t is_responder; + uint8_t discovery_processed; + uint16_t discovery_attempt; + uint16_t tx_pkt; + uint16_t rx_pkt; + uint8_t uapsd_queues; + uint8_t max_sp; + uint8_t buf_sta_capable; + uint8_t off_channel_capable; + uint8_t supported_channels_len; + 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_forced_peer; + uint8_t op_class_for_pref_off_chan; + uint8_t pref_off_chan_num; + uint8_t op_class_for_pref_off_chan_is_set; + qdf_mc_timer_t peer_idle_timer; + bool is_peer_idle_timer_initialised; + uint8_t spatial_streams; + enum tdls_link_state_reason reason; + tdls_state_change_callback state_change_notification; + uint8_t qos; + struct tdls_peer_mlme_info *tdls_info; +}; + +/** + * struct tdls_os_if_event - TDLS os event info + * @type: type of event + * @info: pointer to event information + */ +struct tdls_os_if_event { + uint32_t type; + void *info; +}; + +/** + * enum tdls_os_if_notification - TDLS notification from OS IF + * @TDLS_NOTIFY_STA_SESSION_INCREMENT: sta session count incremented + * @TDLS_NOTIFY_STA_SESSION_DECREMENT: sta session count decremented + */ +enum tdls_os_if_notification { + TDLS_NOTIFY_STA_SESSION_INCREMENT, + TDLS_NOTIFY_STA_SESSION_DECREMENT +}; +/** + * wlan_vdev_get_tdls_soc_obj - private API to get tdls soc object from vdev + * @vdev: vdev object + * + * Return: tdls soc object + */ +static inline struct tdls_soc_priv_obj * +wlan_vdev_get_tdls_soc_obj(struct wlan_objmgr_vdev *vdev) +{ + struct wlan_objmgr_psoc *psoc; + struct tdls_soc_priv_obj *soc_obj; + + if (!vdev) { + tdls_err("NULL vdev"); + return NULL; + } + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + tdls_err("can't get psoc"); + return NULL; + } + + soc_obj = (struct tdls_soc_priv_obj *) + wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_TDLS); + + return soc_obj; +} + +/** + * wlan_psoc_get_tdls_soc_obj - private API to get tdls soc object from psoc + * @psoc: psoc object + * + * Return: tdls soc object + */ +static inline struct tdls_soc_priv_obj * +wlan_psoc_get_tdls_soc_obj(struct wlan_objmgr_psoc *psoc) +{ + struct tdls_soc_priv_obj *soc_obj; + if (!psoc) { + tdls_err("NULL psoc"); + return NULL; + } + soc_obj = (struct tdls_soc_priv_obj *) + wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_TDLS); + + return soc_obj; +} + +/** + * wlan_vdev_get_tdls_vdev_obj - private API to get tdls vdev object from vdev + * @vdev: vdev object + * + * Return: tdls vdev object + */ +static inline struct tdls_vdev_priv_obj * +wlan_vdev_get_tdls_vdev_obj(struct wlan_objmgr_vdev *vdev) +{ + struct tdls_vdev_priv_obj *vdev_obj; + + if (!vdev) { + tdls_err("NULL vdev"); + return NULL; + } + + vdev_obj = (struct tdls_vdev_priv_obj *) + wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_TDLS); + + return vdev_obj; +} + +/** + * tdls_set_link_status - tdls set link status + * @vdev: vdev object + * @mac: mac address of tdls peer + * @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_state link_state, + enum tdls_link_state_reason link_reason); +/** + * tdls_psoc_obj_create_notification() - tdls psoc create notification handler + * @psoc: psoc object + * @arg_list: Argument list + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_psoc_obj_create_notification(struct wlan_objmgr_psoc *psoc, + void *arg_list); + +/** + * tdls_psoc_obj_destroy_notification() - tdls psoc destroy notification handler + * @psoc: psoc object + * @arg_list: Argument list + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_psoc_obj_destroy_notification(struct wlan_objmgr_psoc *psoc, + void *arg_list); + +/** + * tdls_vdev_obj_create_notification() - tdls vdev create notification handler + * @vdev: vdev object + * @arg_list: Argument list + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_vdev_obj_create_notification(struct wlan_objmgr_vdev *vdev, + void *arg_list); + +/** + * tdls_vdev_obj_destroy_notification() - tdls vdev destroy notification handler + * @vdev: vdev object + * @arg_list: Argument list + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_vdev_obj_destroy_notification(struct wlan_objmgr_vdev *vdev, + void *arg_list); + +/** + * tdls_process_cmd() - tdls main command process function + * @msg: scheduler msg + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_process_cmd(struct scheduler_msg *msg); + +/** + * tdls_process_evt() - tdls main event process function + * @msg: scheduler msg + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_process_evt(struct scheduler_msg *msg); + +/** + * tdls_timer_restart() - restart TDLS timer + * @vdev: VDEV object manager + * @timer: timer to restart + * @expiration_time: new expiration time to set for the timer + * + * Return: Void + */ +void tdls_timer_restart(struct wlan_objmgr_vdev *vdev, + qdf_mc_timer_t *timer, + uint32_t expiration_time); + +/** + * wlan_hdd_tdls_timers_stop() - stop all the tdls timers running + * @tdls_vdev: TDLS vdev + * + * Return: none + */ +void tdls_timers_stop(struct tdls_vdev_priv_obj *tdls_vdev); + +/** + * tdls_get_vdev_objects() - Get TDLS private objects + * @vdev: VDEV object manager + * @tdls_vdev_obj: tdls vdev object + * @tdls_soc_obj: tdls soc object + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_get_vdev_objects(struct wlan_objmgr_vdev *vdev, + struct tdls_vdev_priv_obj **tdls_vdev_obj, + struct tdls_soc_priv_obj **tdls_soc_obj); + +/** + * cds_set_tdls_ct_mode() - Set the tdls connection tracker mode + * @hdd_ctx: hdd context + * + * This routine is called to set the tdls connection tracker operation status + * + * Return: NONE + */ +void tdls_set_ct_mode(struct wlan_objmgr_psoc *psoc); + +/** + * tdls_set_operation_mode() - set tdls operating mode + * @tdls_set_mode: tdls mode set params + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_set_operation_mode(struct tdls_set_mode_params *tdls_set_mode); + +/** + * tdls_notify_sta_connect() - Update tdls state for every + * connect event. + * @notify: sta connect params + * + * After every connect event in the system, check whether TDLS + * can be enabled in the system. If TDLS can be enabled, update the + * TDLS state as needed. + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_notify_sta_connect(struct tdls_sta_notify_params *notify); + +/** + * tdls_notify_sta_disconnect() - Update tdls state for every + * disconnect event. + * @notify: sta disconnect params + * + * After every disconnect event in the system, check whether TDLS + * can be disabled/enabled in the system and update the + * TDLS state as needed. + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_notify_sta_disconnect(struct tdls_sta_notify_params *notify); + +/** + * tdls_notify_reset_adapter() - notify reset adapter + * @vdev: vdev object + * + * Notify TDLS about the adapter reset + * + * Return: None + */ +void tdls_notify_reset_adapter(struct wlan_objmgr_vdev *vdev); + +/** + * tdls_peers_deleted_notification() - peer delete notification + * @psoc: soc object + * @vdev_id: vdev id + * + * Legacy lim layer will delete tdls peers for roaming and heart beat failures + * and notify the component about the delete event to update the tdls. + * state. + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_peers_deleted_notification(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); + +/** + * tdls_notify_decrement_session() - Notify the session decrement + * @psoc: psoc object manager + * + * Policy manager notify TDLS about session decrement + * + * Return: None + */ +void tdls_notify_decrement_session(struct wlan_objmgr_psoc *psoc); + +/** + * tdls_send_update_to_fw - update tdls status info + * @tdls_vdev_obj: tdls vdev private object. + * @tdls_prohibited: indicates whether tdls is prohibited. + * @tdls_chan_swit_prohibited: indicates whether tdls channel switch + * is prohibited. + * @sta_connect_event: indicate sta connect or disconnect event + * @session_id: session id + * + * Normally an AP does not influence TDLS connection between STAs + * associated to it. But AP may set bits for TDLS Prohibited or + * TDLS Channel Switch Prohibited in Extended Capability IE in + * Assoc/Re-assoc response to STA. So after STA is connected to + * an AP, call this function to update TDLS status as per those + * bits set in Ext Cap IE in received Assoc/Re-assoc response + * from AP. + * + * Return: None. + */ +void tdls_send_update_to_fw(struct tdls_vdev_priv_obj *tdls_vdev_obj, + struct tdls_soc_priv_obj *tdls_soc_obj, + bool tdls_prohibited, + bool tdls_chan_swit_prohibited, + bool sta_connect_event, + uint8_t session_id); + +/** + * tdls_notify_increment_session() - Notify the session increment + * @psoc: psoc object manager + * + * Policy manager notify TDLS about session increment + * + * Return: None + */ +void tdls_notify_increment_session(struct wlan_objmgr_psoc *psoc); + +/** + * tdls_check_is_tdls_allowed() - check is tdls allowed or not + * @vdev: vdev object + * + * Function determines the whether TDLS allowed in the system + * + * Return: true or false + */ +bool tdls_check_is_tdls_allowed(struct wlan_objmgr_vdev *vdev); + +/** + * tdls_get_vdev() - Get tdls specific vdev object manager + * @psoc: wlan psoc object manager + * @dbg_id: debug id + * + * If TDLS possible, return the corresponding vdev + * to enable TDLS in the system. + * + * Return: vdev manager pointer or NULL. + */ +struct wlan_objmgr_vdev *tdls_get_vdev(struct wlan_objmgr_psoc *psoc, + wlan_objmgr_ref_dbgid dbg_id); + +/** + * tdls_process_policy_mgr_notification() - process policy manager notification + * @psoc: soc object manager + * + * Return: QDF_STATUS + */ +QDF_STATUS +tdls_process_policy_mgr_notification(struct wlan_objmgr_psoc *psoc); + +/** + * tdls_scan_complete_event_handler() - scan complete event handler for tdls + * @vdev: vdev object + * @event: scan event + * @arg: tdls soc object + * + * Return: None + */ +void tdls_scan_complete_event_handler(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, + void *arg); + +/** + * tdls_scan_callback() - callback for TDLS scan operation + * @soc: tdls soc pvt object + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_scan_callback(struct tdls_soc_priv_obj *tdls_soc); + +/** + * wlan_hdd_tdls_scan_done_callback() - callback for tdls scan done event + * @tdls_soc: tdls soc object + * + * Return: Void + */ +void tdls_scan_done_callback(struct tdls_soc_priv_obj *tdls_soc); + +/** + * tdls_scan_serialization_comp_info_cb() - callback for scan start + * @vdev: VDEV on which the scan command is being processed + * @comp_info: serialize rules info + * + * Return: negative = caller should stop and return error code immediately + * 1 = caller can continue to scan + */ +void tdls_scan_serialization_comp_info_cb(struct wlan_objmgr_vdev *vdev, + union wlan_serialization_rules_info *comp_info); + +/** + * tdls_set_offchan_mode() - update tdls status info + * @psoc: soc object + * @param: channel switch params + * + * send message to WMI to set TDLS off channel in f/w + * + * Return: QDF_STATUS. + */ +QDF_STATUS tdls_set_offchan_mode(struct wlan_objmgr_psoc *psoc, + struct tdls_channel_switch_params *param); + +/** + * tdls_delete_all_peers_indication() - update tdls status info + * @psoc: soc object + * @vdev_id: vdev id + * + * Notify tdls component to cleanup all peers + * + * Return: QDF_STATUS. + */ + +QDF_STATUS tdls_delete_all_peers_indication(struct wlan_objmgr_psoc *psoc, + uint8_t vdev_id); +#endif diff --git a/components/tdls/core/src/wlan_tdls_mgmt.c b/components/tdls/core/src/wlan_tdls_mgmt.c new file mode 100644 index 000000000000..47b30c4f9bc6 --- /dev/null +++ b/components/tdls/core/src/wlan_tdls_mgmt.c @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_mgmt.c + * + * TDLS management frames implementation + */ + +#include "wlan_tdls_main.h" +#include "wlan_tdls_tgt_api.h" +#include +#include "wlan_mgmt_txrx_utils_api.h" +#include "wlan_tdls_peer.h" +#include "wlan_tdls_ct.h" +#include "wlan_tdls_cmds_process.h" +#include "wlan_tdls_mgmt.h" + +static +const char *const tdls_action_frames_type[] = { "TDLS Setup Request", + "TDLS Setup Response", + "TDLS Setup Confirm", + "TDLS Teardown", + "TDLS Peer Traffic Indication", + "TDLS Channel Switch Request", + "TDLS Channel Switch Response", + "TDLS Peer PSM Request", + "TDLS Peer PSM Response", + "TDLS Peer Traffic Response", + "TDLS Discovery Request"}; + +/** + * tdls_set_rssi() - Set TDLS RSSI on peer given by mac + * @tdls_vdev: tdls vdev object + * @mac: MAC address of Peer + * @rx_rssi: rssi value + * + * Set RSSI on TDSL peer + * + * Return: 0 for success or -EINVAL otherwise + */ +static int tdls_set_rssi(struct tdls_vdev_priv_obj *tdls_vdev, + const uint8_t *mac, + int8_t rx_rssi) +{ + struct tdls_peer *curr_peer; + + curr_peer = tdls_find_peer(tdls_vdev, mac); + if (curr_peer == NULL) { + tdls_err("curr_peer is NULL"); + return -EINVAL; + } + + curr_peer->rssi = rx_rssi; + + return 0; +} + +/** + * tdls_process_rx_mgmt() - process tdls rx mgmt frames + * @rx_mgmt_event: tdls rx mgmt event + * @tdls_vdev: tdls vdev object + * + * Return: QDF_STATUS + */ +static QDF_STATUS tdls_process_rx_mgmt( + struct tdls_rx_mgmt_event *rx_mgmt_event, + struct tdls_vdev_priv_obj *tdls_vdev) +{ + struct tdls_rx_mgmt_frame *rx_mgmt; + struct tdls_soc_priv_obj *tdls_soc_obj; + uint8_t *mac; + enum tdls_actioncode action_frame_type; + + if (!rx_mgmt_event) + return QDF_STATUS_E_INVAL; + + tdls_soc_obj = rx_mgmt_event->tdls_soc_obj; + rx_mgmt = rx_mgmt_event->rx_mgmt; + + if (!tdls_soc_obj || !rx_mgmt) { + tdls_err("invalid psoc object or rx mgmt"); + return QDF_STATUS_E_INVAL; + } + + tdls_debug("soc:%pK, frame_len:%d, rx_chan:%d, vdev_id:%d, frm_type:%d, rx_rssi:%d, buf:%pK", + tdls_soc_obj->soc, rx_mgmt->frame_len, + rx_mgmt->rx_chan, rx_mgmt->vdev_id, rx_mgmt->frm_type, + rx_mgmt->rx_rssi, rx_mgmt->buf); + + if (rx_mgmt->buf[TDLS_PUBLIC_ACTION_FRAME_OFFSET + 1] == + TDLS_PUBLIC_ACTION_DISC_RESP) { + mac = &rx_mgmt->buf[TDLS_80211_PEER_ADDR_OFFSET]; + tdls_notice("[TDLS] TDLS Discovery Response," + QDF_MAC_ADDR_STR " RSSI[%d] <--- OTA", + QDF_MAC_ADDR_ARRAY(mac), rx_mgmt->rx_rssi); + tdls_recv_discovery_resp(tdls_vdev, mac); + tdls_set_rssi(tdls_vdev, mac, rx_mgmt->rx_rssi); + } + + if (rx_mgmt->buf[TDLS_PUBLIC_ACTION_FRAME_OFFSET] == + TDLS_ACTION_FRAME) { + action_frame_type = + rx_mgmt->buf[TDLS_PUBLIC_ACTION_FRAME_OFFSET + 1]; + if (action_frame_type >= TDLS_ACTION_FRAME_TYPE_MAX) { + tdls_debug("[TDLS] unknown[%d] <--- OTA", + action_frame_type); + } else { + tdls_notice("[TDLS] %s <--- OTA", + tdls_action_frames_type[action_frame_type]); + } + } + + /* tdls_soc_obj->tdls_rx_cb ==> wlan_cfg80211_tdls_rx_callback() */ + if (tdls_soc_obj && tdls_soc_obj->tdls_rx_cb) + tdls_soc_obj->tdls_rx_cb(tdls_soc_obj->tdls_rx_cb_data, + rx_mgmt); + else + tdls_debug("rx mgmt, but no valid up layer callback"); + + qdf_mem_free(rx_mgmt); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tdls_process_rx_frame(struct scheduler_msg *msg) +{ + struct wlan_objmgr_vdev *vdev; + struct tdls_rx_mgmt_event *tdls_rx; + struct tdls_vdev_priv_obj *tdls_vdev; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (!(msg->bodyptr)) { + tdls_err("invalid message body"); + return QDF_STATUS_E_INVAL; + } + + tdls_rx = (struct tdls_rx_mgmt_event *) msg->bodyptr; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(tdls_rx->tdls_soc_obj->soc, + tdls_rx->rx_mgmt->vdev_id, WLAN_TDLS_NB_ID); + + if (vdev) { + tdls_debug("tdls rx mgmt frame received"); + tdls_vdev = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_TDLS); + if (tdls_vdev) + status = tdls_process_rx_mgmt(tdls_rx, tdls_vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + } + + qdf_mem_free(msg->bodyptr); + msg->bodyptr = NULL; + + return status; +} + +QDF_STATUS tdls_mgmt_rx_ops(struct wlan_objmgr_psoc *psoc, + bool isregister) +{ + struct mgmt_txrx_mgmt_frame_cb_info frm_cb_info; + QDF_STATUS status; + int num_of_entries; + + tdls_debug("psoc:%pK, is register rx:%d", psoc, isregister); + + frm_cb_info.frm_type = MGMT_ACTION_TDLS_DISCRESP; + frm_cb_info.mgmt_rx_cb = tgt_tdls_mgmt_frame_rx_cb; + num_of_entries = 1; + + if (isregister) + status = wlan_mgmt_txrx_register_rx_cb(psoc, + WLAN_UMAC_COMP_TDLS, &frm_cb_info, + num_of_entries); + else + status = wlan_mgmt_txrx_deregister_rx_cb(psoc, + WLAN_UMAC_COMP_TDLS, &frm_cb_info, + num_of_entries); + + return status; +} + +static QDF_STATUS +tdls_internal_send_mgmt_tx_done(struct tdls_action_frame_request *req, + QDF_STATUS status) +{ + struct tdls_soc_priv_obj *tdls_soc_obj; + struct tdls_osif_indication indication; + + if (!req || !req->vdev) + return QDF_STATUS_E_NULL_VALUE; + + indication.status = status; + indication.vdev = req->vdev; + tdls_soc_obj = wlan_vdev_get_tdls_soc_obj(req->vdev); + if (tdls_soc_obj && tdls_soc_obj->tdls_event_cb) + tdls_soc_obj->tdls_event_cb(tdls_soc_obj->tdls_evt_cb_data, + TDLS_EVENT_MGMT_TX_ACK_CNF, &indication); + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS tdls_activate_send_mgmt_request_flush_cb( + struct scheduler_msg *msg) +{ + struct tdls_send_mgmt_request *tdls_mgmt_req; + + tdls_mgmt_req = msg->bodyptr; + + qdf_mem_free(tdls_mgmt_req); + msg->bodyptr = NULL; + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS tdls_activate_send_mgmt_request( + struct tdls_action_frame_request *action_req) +{ + struct wlan_objmgr_peer *peer; + struct tdls_soc_priv_obj *tdls_soc_obj; + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct scheduler_msg msg = {0}; + struct tdls_send_mgmt_request *tdls_mgmt_req; + + if (!action_req || !action_req->vdev) + return QDF_STATUS_E_NULL_VALUE; + + tdls_soc_obj = wlan_vdev_get_tdls_soc_obj(action_req->vdev); + if (!tdls_soc_obj) { + status = QDF_STATUS_E_NULL_VALUE; + goto release_cmd; + } + + tdls_mgmt_req = qdf_mem_malloc(sizeof(struct tdls_send_mgmt_request) + + action_req->tdls_mgmt.len); + if (NULL == tdls_mgmt_req) { + status = QDF_STATUS_E_NOMEM; + tdls_err("mem alloc failed "); + QDF_ASSERT(0); + goto release_cmd; + } + + tdls_debug("session_id %d " + "tdls_mgmt.dialog %d " + "tdls_mgmt.frame_type %d " + "tdls_mgmt.status_code %d " + "tdls_mgmt.responder %d " + "tdls_mgmt.peer_capability %d", + action_req->session_id, + action_req->tdls_mgmt.dialog, + action_req->tdls_mgmt.frame_type, + action_req->tdls_mgmt.status_code, + action_req->tdls_mgmt.responder, + action_req->tdls_mgmt.peer_capability); + + tdls_mgmt_req->session_id = action_req->session_id; + /* Using dialog as transactionId. This can be used to + * match response with request + */ + tdls_mgmt_req->transaction_id = action_req->tdls_mgmt.dialog; + tdls_mgmt_req->req_type = action_req->tdls_mgmt.frame_type; + tdls_mgmt_req->dialog = action_req->tdls_mgmt.dialog; + tdls_mgmt_req->status_code = action_req->tdls_mgmt.status_code; + tdls_mgmt_req->responder = action_req->tdls_mgmt.responder; + tdls_mgmt_req->peer_capability = action_req->tdls_mgmt.peer_capability; + + peer = wlan_vdev_get_bsspeer(action_req->vdev); + + status = wlan_objmgr_peer_try_get_ref(peer, WLAN_TDLS_SB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(tdls_mgmt_req); + goto release_cmd; + } + + qdf_mem_copy(tdls_mgmt_req->bssid.bytes, + wlan_peer_get_macaddr(peer), QDF_MAC_ADDR_SIZE); + + qdf_mem_copy(tdls_mgmt_req->peer_mac.bytes, + action_req->tdls_mgmt.peer_mac.bytes, QDF_MAC_ADDR_SIZE); + + if (action_req->tdls_mgmt.len) { + qdf_mem_copy(tdls_mgmt_req->add_ie, action_req->tdls_mgmt.buf, + action_req->tdls_mgmt.len); + } + + tdls_mgmt_req->length = sizeof(struct tdls_send_mgmt_request) + + action_req->tdls_mgmt.len; + if (action_req->use_default_ac) + tdls_mgmt_req->ac = WIFI_AC_VI; + else + tdls_mgmt_req->ac = WIFI_AC_BK; + + /* Send the request to PE. */ + qdf_mem_zero(&msg, sizeof(msg)); + + tdls_debug("sending TDLS Mgmt Frame req to PE "); + tdls_mgmt_req->message_type = tdls_soc_obj->tdls_send_mgmt_req; + + msg.type = tdls_soc_obj->tdls_send_mgmt_req; + msg.bodyptr = tdls_mgmt_req; + msg.flush_callback = tdls_activate_send_mgmt_request_flush_cb; + + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_PE, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("failed to post msg, status %d", status); + qdf_mem_free(tdls_mgmt_req); + } + + wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_SB_ID); + +release_cmd: + /*update tdls nss infornation based on action code */ + tdls_reset_nss(tdls_soc_obj, action_req->chk_frame->action_code); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_internal_send_mgmt_tx_done(action_req, status); + tdls_release_serialization_command(action_req->vdev, + WLAN_SER_CMD_TDLS_SEND_MGMT); + } + + return status; +} + +static QDF_STATUS +tdls_send_mgmt_serialize_callback(struct wlan_serialization_command *cmd, + enum wlan_serialization_cb_reason reason) +{ + struct tdls_action_frame_request *req; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!cmd || !cmd->umac_cmd) { + tdls_err("invalid params cmd: %pK, ", cmd); + return QDF_STATUS_E_NULL_VALUE; + } + req = cmd->umac_cmd; + + tdls_debug("reason: %d, vdev_id: %d", + reason, req->vdev_id); + + switch (reason) { + case WLAN_SER_CB_ACTIVATE_CMD: + /* command moved to active list */ + status = tdls_activate_send_mgmt_request(req); + break; + + case WLAN_SER_CB_CANCEL_CMD: + case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT: + /* command removed from pending list. + * notify status complete with failure + */ + status = tdls_internal_send_mgmt_tx_done(req, + QDF_STATUS_E_FAILURE); + break; + + case WLAN_SER_CB_RELEASE_MEM_CMD: + /* command successfully completed. + * release tdls_action_frame_request memory + */ + 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_mgmt_req( + struct tdls_action_frame_request *tdls_mgmt_req) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct wlan_serialization_command cmd = {0, }; + enum wlan_serialization_status ser_cmd_status; + + /* If connected and in Infra. Only then allow this */ + status = tdls_validate_mgmt_request(tdls_mgmt_req); + if (status != QDF_STATUS_SUCCESS) { + status = tdls_internal_send_mgmt_tx_done(tdls_mgmt_req, + status); + goto error_mgmt; + } + + /* update the responder, status code information + * after the cmd validation + */ + tdls_mgmt_req->tdls_mgmt.responder = + !tdls_mgmt_req->chk_frame->responder; + tdls_mgmt_req->tdls_mgmt.status_code = + tdls_mgmt_req->chk_frame->status_code; + + cmd.cmd_type = WLAN_SER_CMD_TDLS_SEND_MGMT; + /* Cmd Id not applicable for non scan cmds */ + cmd.cmd_id = 0; + cmd.cmd_cb = (wlan_serialization_cmd_callback) + tdls_send_mgmt_serialize_callback; + cmd.umac_cmd = tdls_mgmt_req; + cmd.source = WLAN_UMAC_COMP_TDLS; + cmd.is_high_priority = false; + cmd.cmd_timeout_duration = TDLS_DEFAULT_SERIALIZE_CMD_TIMEOUT; + + cmd.vdev = tdls_mgmt_req->vdev; + cmd.is_blocking = true; + + ser_cmd_status = wlan_serialization_request(&cmd); + tdls_debug("wlan_serialization_request status:%d", 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: + status = QDF_STATUS_E_FAILURE; + goto error_mgmt; + default: + QDF_ASSERT(0); + status = QDF_STATUS_E_INVAL; + goto error_mgmt; + } + return status; + +error_mgmt: + wlan_objmgr_vdev_release_ref(tdls_mgmt_req->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(tdls_mgmt_req); + return status; +} diff --git a/components/tdls/core/src/wlan_tdls_mgmt.h b/components/tdls/core/src/wlan_tdls_mgmt.h new file mode 100644 index 000000000000..ce6f9307988f --- /dev/null +++ b/components/tdls/core/src/wlan_tdls_mgmt.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_mgmt.h + * + * TDLS management frames include file + */ + +#ifndef _WLAN_TDLS_MGMT_H_ +#define _WLAN_TDLS_MGMT_H_ + +#define TDLS_PUBLIC_ACTION_FRAME_OFFSET 24 +#define TDLS_PUBLIC_ACTION_FRAME 4 +#define TDLS_PUBLIC_ACTION_DISC_RESP 14 +#define TDLS_ACTION_FRAME 12 +#define TDLS_80211_PEER_ADDR_OFFSET (TDLS_PUBLIC_ACTION_FRAME + \ + QDF_MAC_ADDR_SIZE) +#define TDLS_ACTION_FRAME_TYPE_MAX 11 + +/** + * struct tdls_rx_mgmt_event - tdls rx mgmt frame event + * @tdls_soc_obj: tdls soc private object + * @rx_mgmt: tdls rx mgmt frame structure + */ +struct tdls_rx_mgmt_event { + struct tdls_soc_priv_obj *tdls_soc_obj; + struct tdls_rx_mgmt_frame *rx_mgmt; +}; + +/* + * struct tdls_send_mgmt_request - tdls management request + * @message_type: type of pe message + * @length: length of the frame. + * @session_id: session id + * @transaction_id: transaction ID for cmd + * @req_type: type of action frame + * @dialog: dialog token used in the frame. + * @status_code: status to be incuded in the frame. + * @responder: tdls request type + * @peer_capability: peer capability information + * @bssid: bssid + * @peer_mac: mac address of the peer + * @add_ie: additional ie's to be included + */ +struct tdls_send_mgmt_request { + uint16_t message_type; + uint16_t length; + uint8_t session_id; + uint16_t transaction_id; + uint8_t req_type; + uint8_t dialog; + uint16_t status_code; + uint8_t responder; + uint32_t peer_capability; + struct qdf_mac_addr bssid; + struct qdf_mac_addr peer_mac; + enum wifi_traffic_ac ac; + /* Variable length. Dont add any field after this. */ + uint8_t add_ie[1]; +}; + +/** + * tdls_process_mgmt_req() - send a TDLS mgmt request to serialize module + * @tdls_mgmt_req: tdls management request + * + * TDLS request API, called from cfg80211 to send a TDLS frame in + * serialized manner to PE + * + *Return: QDF_STATUS + */ +QDF_STATUS tdls_process_mgmt_req( + struct tdls_action_frame_request *tdls_mgmt_req); + +/** + * tdls_mgmt_rx_ops() - register or unregister rx callback + * @psoc: psoc object + * @isregister: register if true, unregister if false + * + * This function registers or unregisters rx callback to mgmt txrx + * component. + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_mgmt_rx_ops(struct wlan_objmgr_psoc *psoc, + bool isregister); + +/** + * tdls_process_rx_frame() - process tdls rx frames + * @msg: scheduler msg + * + * Return: QDF_STATUS + */ +QDF_STATUS tdls_process_rx_frame(struct scheduler_msg *msg); +#endif + diff --git a/components/tdls/core/src/wlan_tdls_peer.c b/components/tdls/core/src/wlan_tdls_peer.c new file mode 100644 index 000000000000..ff4bf86379a4 --- /dev/null +++ b/components/tdls/core/src/wlan_tdls_peer.c @@ -0,0 +1,819 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_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_ADDR_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; +} + +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); + peer->sta_id = INVALID_TDLS_PEER_ID; + + qdf_list_insert_back(head, &peer->node); + + tdls_debug("add tdls peer: " QDF_MAC_ADDR_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_ADDR_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; + } + + psoc = wlan_vdev_get_psoc(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_ADDR_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 QDF_OPMODE 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) { + vdev_id = wlan_vdev_get_id(vdev_obj->vdev); + opmode = wlan_vdev_mlme_get_opmode(vdev_obj->vdev); + + *channel = policy_mgr_get_channel(soc_obj->soc, + policy_mgr_convert_device_mode_to_qdf_type(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: %pK, pdev: %pK", soc_obj, pdev); + return; + } + + qdf_mem_zero(peer_param, sizeof(*peer_param)); + peer_param->vdev_id = wlan_vdev_get_id(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.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_reg_is_dsrc_chan(pdev, 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; + peer_param->peer_cap.peer_chanlen++; + 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 = 0; + uint8_t is_off_channel_supported = 0; + uint8_t is_qos_wmm_sta = 0; + 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 = 1; + + 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_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 = INVALID_TDLS_PEER_ID; + + 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_ADDR_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/components/tdls/core/src/wlan_tdls_peer.h b/components/tdls/core/src/wlan_tdls_peer.h new file mode 100644 index 000000000000..41ffa8096f54 --- /dev/null +++ b/components/tdls/core/src/wlan_tdls_peer.h @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_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_all_peer() - find peer matching the input MACaddr in soc range + * @soc_obj: TDLS soc object + * @channel:channel number + * @bw_offset: offset to bandwidth + * + * This is in scheduler thread context, no lock required. + * + * Return: Operating class + */ +uint8_t tdls_find_opclass(struct wlan_objmgr_psoc *psoc, + uint8_t channel, + uint8_t bw_offset); + +/** + * 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_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/components/tdls/core/src/wlan_tdls_txrx.c b/components/tdls/core/src/wlan_tdls_txrx.c new file mode 100644 index 000000000000..6ded57c974f8 --- /dev/null +++ b/components/tdls/core/src/wlan_tdls_txrx.c @@ -0,0 +1,23 @@ +/* + * 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_txrx.c + * + * TDLS txrx function definitions + */ diff --git a/components/tdls/core/src/wlan_tdls_txrx.h b/components/tdls/core/src/wlan_tdls_txrx.h new file mode 100644 index 000000000000..b001ffe74599 --- /dev/null +++ b/components/tdls/core/src/wlan_tdls_txrx.h @@ -0,0 +1,23 @@ +/* + * 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_txrx.h + * + * TDLS txrx api declaration + */ diff --git a/components/tdls/dispatcher/inc/wlan_tdls_cfg.h b/components/tdls/dispatcher/inc/wlan_tdls_cfg.h new file mode 100644 index 000000000000..03827e593a58 --- /dev/null +++ b/components/tdls/dispatcher/inc/wlan_tdls_cfg.h @@ -0,0 +1,759 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(CONFIG_TDLS_H__) +#define CONFIG_TDLS_H__ + +#include "cfg_define.h" +#include "cfg_converged.h" +#include "qdf_types.h" + +/* + * + * gTDLSUapsdMask - ACs to setup U-APSD for TDLS Sta. + * @Min: 0 + * @Max: 0x0F + * @Default: 0x0F + * + * This ini is used to configure the ACs for which mask needs to be enabled. + * 0x1: Background 0x2: Best effort + * 0x4: Video 0x8: Voice + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_QOS_WMM_UAPSD_MASK CFG_INI_UINT( \ + "gTDLSUapsdMask", \ + 0, \ + 0x0F, \ + 0x0F, \ + CFG_VALUE_OR_DEFAULT, \ + "ACs to setup U-APSD for TDLS Sta") + +/* + * + * gEnableTDLSBufferSta - Controls the TDLS buffer. + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to control the TDLS buffer. + * Buffer STA is not enabled in CLD 2.0 yet. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_BUF_STA_ENABLED CFG_INI_BOOL( \ + "gEnableTDLSBufferSta", \ + 1, \ + "Controls the TDLS buffer") + +/* + * + * gTDLSPuapsdInactivityTime - Peer UAPSD Inactivity time. + * @Min: 0 + * @Max: 10 + * @Default: 0 + * + * This ini is used to configure peer uapsd inactivity time. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_PUAPSD_INACT_TIME CFG_INI_UINT( \ + "gTDLSPuapsdInactivityTime", \ + 0, \ + 10, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Peer UAPSD Inactivity time") + +/* + * + * gTDLSPuapsdRxFrameThreshold - Peer UAPSD Rx frame threshold. + * @Min: 10 + * @Max: 20 + * @Default: 10 + * + * This ini is used to configure maximum Rx frame during SP. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_RX_FRAME_THRESHOLD CFG_INI_UINT( \ + "gTDLSPuapsdRxFrameThreshold", \ + 10, \ + 20, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "Peer UAPSD Rx frame threshold") + +/* + * + * gEnableTDLSOffChannel - Enables off-channel support for TDLS link. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable off-channel support for TDLS link. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_OFF_CHANNEL_ENABLED CFG_INI_BOOL( \ + "gEnableTDLSOffChannel", \ + 0, \ + "Enables off-channel support for TDLS") + +/* + * + * gEnableTDLSSupport - Enable support for TDLS. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable TDLS support. + * + * Related: None. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_SUPPORT_ENABLE CFG_INI_BOOL( \ + "gEnableTDLSSupport", \ + 0, \ + "enable/disable TDLS support") + +/* + * + * gEnableTDLSImplicitTrigger - Enable Implicit TDLS. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable implicit TDLS. + * CLD driver initiates TDLS Discovery towards a peer whenever TDLS Setup + * criteria (throughput and RSSI thresholds) is met and then it tears down + * TDLS when teardown criteria (idle packet count and RSSI) is met. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_IMPLICIT_TRIGGER CFG_INI_BOOL( \ + "gEnableTDLSImplicitTrigger", \ + 0, \ + "enable/disable implicit TDLS") + +/* + * + * gTDLSTxStatsPeriod - TDLS TX statistics time period. + * @Min: 1000 + * @Max: 4294967295 + * @Default: 2000 + * + * This ini is used to configure the time period (in ms) to evaluate whether + * the number of Tx/Rx packets exceeds TDLSTxPacketThreshold and triggers a + * TDLS Discovery request. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_TX_STATS_PERIOD CFG_INI_UINT( \ + "gTDLSTxStatsPeriod", \ + 1000, \ + 4294967295UL, \ + 2000, \ + CFG_VALUE_OR_DEFAULT, \ + "TDLS TX statistics time period") + +/* + * + * gTDLSTxPacketThreshold - Tx/Rx Packet threshold for initiating TDLS. + * @Min: 0 + * @Max: 4294967295 + * @Default: 40 + * + * This ini is used to configure the number of Tx/Rx packets during the + * period of gTDLSTxStatsPeriod when exceeded, a TDLS Discovery request + * is triggered. + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_TX_PACKET_THRESHOLD CFG_INI_UINT( \ + "gTDLSTxPacketThreshold", \ + 0, \ + 4294967295UL, \ + 40, \ + CFG_VALUE_OR_DEFAULT, \ + "Tx/Rx Packet threshold for initiating TDLS") + +/* + * + * gTDLSMaxDiscoveryAttempt - Attempts for sending TDLS discovery requests. + * @Min: 1 + * @Max: 100 + * @Default: 5 + * + * This ini is used to configure the number of failures of discover request, + * when exceeded, the peer is assumed to be not TDLS capable and no further + * TDLS Discovery request is made. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_MAX_DISCOVERY_ATTEMPT CFG_INI_UINT( \ + "gTDLSMaxDiscoveryAttempt", \ + 1, \ + 100, \ + 5, \ + CFG_VALUE_OR_DEFAULT, \ + "Attempts for sending TDLS discovery requests") + +/* + * + * gTDLSIdleTimeout - Duration within which number of TX / RX frames meet the + * criteria for TDLS teardown. + * @Min: 500 + * @Max: 40000 + * @Default: 5000 + * + * This ini is used to configure the time period (in ms) to evaluate whether + * the number of Tx/Rx packets exceeds gTDLSIdlePacketThreshold and thus meets + * criteria for TDLS teardown. + * Teardown notification interval (gTDLSIdleTimeout) should be multiple of + * setup notification (gTDLSTxStatsPeriod) interval. + * e.g. + * if setup notification (gTDLSTxStatsPeriod) interval = 500, then + * teardown notification (gTDLSIdleTimeout) interval should be 1000, + * 1500, 2000, 2500... + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + */ +#define CFG_TDLS_IDLE_TIMEOUT CFG_INI_UINT( \ + "gTDLSIdleTimeout", \ + 500, \ + 40000, \ + 5000, \ + CFG_VALUE_OR_DEFAULT, \ + "this is idle time period") + +/* + * + * gTDLSIdlePacketThreshold - Number of idle packet. + * @Min: 0 + * @Max: 40000 + * @Default: 3 + * + * This ini is used to configure the number of Tx/Rx packet, below which + * within last gTDLSTxStatsPeriod period is considered as idle condition. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_IDLE_PACKET_THRESHOLD CFG_INI_UINT( \ + "gTDLSIdlePacketThreshold", \ + 0, \ + 40000, \ + 3, \ + CFG_VALUE_OR_DEFAULT, \ + "Number of idle packet") + +/* + * + * gTDLSRSSITriggerThreshold - RSSI threshold for TDLS connection. + * @Min: -120 + * @Max: 0 + * @Default: -75 + * + * This ini is used to configure the absolute value (in dB) of the peer RSSI, + * below which a TDLS setup request is triggered. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_RSSI_TRIGGER_THRESHOLD CFG_INI_INT( \ + "gTDLSRSSITriggerThreshold", \ + -120, \ + 0, \ + -75, \ + CFG_VALUE_OR_DEFAULT, \ + "RSSI threshold for TDLS connection") + +/* + * + * gTDLSRSSITeardownThreshold - RSSI threshold for TDLS teardown. + * @Min: -120 + * @Max: 0 + * @Default: -75 + * + * This ini is used to configure the absolute value (in dB) of the peer RSSI, + * when exceed, a TDLS teardown is triggered. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_RSSI_TEARDOWN_THRESHOLD CFG_INI_INT( \ + "gTDLSRSSITeardownThreshold", \ + -120, \ + 0, \ + -75, \ + CFG_VALUE_OR_DEFAULT, \ + "RSSI threshold for TDLS teardown") + +/* + * + * gTDLSRSSITeardownThreshold - RSSI threshold for TDLS teardown. + * @Min: -120 + * @Max: 0 + * @Default: -75 + * + * This ini is used to configure the absolute value (in dB) of the peer RSSI, + * when exceed, a TDLS teardown is triggered. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_RSSI_DELTA CFG_INI_INT( \ + "gTDLSRSSIDelta", \ + -30, \ + 0, \ + -20, \ + CFG_VALUE_OR_DEFAULT, \ + "Delta value for the peer RSSI that can trigger teardown") + +/* + * + * gTDLSPrefOffChanNum - Preferred TDLS channel number when off-channel support + * is enabled. + * @Min: 1 + * @Max: 165 + * @Default: 36 + * + * This ini is used to configure preferred TDLS channel number when off-channel + * support is enabled. + * + * Related: gEnableTDLSSupport, gEnableTDLSOffChannel. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_PREFERRED_OFF_CHANNEL_NUM CFG_INI_UINT( \ + "gTDLSPrefOffChanNum", \ + 1, \ + 165, \ + 36, \ + CFG_VALUE_OR_DEFAULT, \ + "Preferred TDLS channel number") + +/* + * + * gTDLSPrefOffChanBandwidth - Preferred TDLS channel bandwidth when + * off-channel support is enabled. + * @Min: 0x01 + * @Max: 0x0F + * @Default: 0x07 + * + * This ini is used to configure preferred TDLS channel bandwidth when + * off-channel support is enabled. + * 0x1: 20 MHz 0x2: 40 MHz 0x4: 80 MHz 0x8: 160 MHz + * When more than one bits are set then firmware starts from the highest and + * selects one based on capability of peer. + * + * Related: gEnableTDLSSupport, gEnableTDLSOffChannel. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_PREFERRED_OFF_CHANNEL_BW CFG_INI_UINT( \ + "gTDLSPrefOffChanBandwidth", \ + 1, \ + 15, \ + 7, \ + CFG_VALUE_OR_DEFAULT, \ + "Preferred TDLS channel bandwidth") + +/* + * + * gTDLSPuapsdInactivityTime - Peer UAPSD Inactivity time. + * @Min: 0 + * @Max: 10 + * @Default: 0 + * + * This ini is used to configure peer uapsd inactivity time. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_PUAPSD_INACTIVITY_TIME CFG_INI_UINT( \ + "gTDLSPuapsdInactivityTime", \ + 0, \ + 10, \ + 0, \ + CFG_VALUE_OR_DEFAULT, \ + "Peer UAPSD Inactivity time") + +/* + * + * gTDLSPuapsdInactivityTime - Peer UAPSD Inactivity time. + * @Min: 0 + * @Max: 10 + * @Default: 0 + * + * This ini is used to configure peer uapsd inactivity time. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_PUAPSD_RX_FRAME_THRESHOLD CFG_INI_UINT( \ + "gTDLSPuapsdRxFrameThreshold", \ + 10, \ + 20, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "Peer UAPSD Rx frame threshold") + +/* + * + * gTDLSPuapsdPTIWindow - This ini is used to configure peer traffic indication + * window. + * @Min: 1 + * @Max: 5 + * @Default: 2 + * + * This ini is used to configure buffering time in number of beacon intervals. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_PUAPSD_PEER_TRAFFIC_IND_WINDOW CFG_INI_UINT( \ + "gTDLSPuapsdPTIWindow", \ + 1, \ + 5, \ + 2, \ + CFG_VALUE_OR_DEFAULT, \ + "This ini is used to configure peer traffic indication") + +/* + * + * gTDLSPuapsdPTIWindow - This ini is used to configure peer traffic indication + * window. + * @Min: 1 + * @Max: 5 + * @Default: 2 + * + * This ini is used to configure buffering time in number of beacon intervals. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_PUAPSD_PEER_TRAFFIC_RSP_TIMEOUT CFG_INI_UINT( \ + "gTDLSPuapsdPTRTimeout", \ + 0, \ + 10000, \ + 5000, \ + CFG_VALUE_OR_DEFAULT, \ + "Peer Traffic Response timer duration in ms") + +/* + * + * gTDLSPuapsdPTIWindow - This ini is used to configure peer traffic indication + * window. + * @Min: 1 + * @Max: 5 + * @Default: 2 + * + * This ini is used to configure buffering time in number of beacon intervals. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_EXTERNAL_CONTROL CFG_INI_BOOL( \ + "gTDLSExternalControl", \ + 1, \ + "Enable external TDLS control") + +/* + * + * gEnableTDLSWmmMode - Enables WMM support over TDLS link. + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable WMM support over TDLS link. + * This is required to be set to 1 for any TDLS and uAPSD functionality. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_WMM_MODE_ENABLE CFG_INI_BOOL( \ + "gEnableTDLSWmmMode", \ + 1, \ + "Enables WMM support over TDLS link") + +/* + * + * gEnableTDLSScan - Allow scan and maintain TDLS link. + * @Min: 0 + * @Max: 1 + * @Default: 0 + * + * This ini is used to enable/disable TDLS scan. + * 0: If peer is not buffer STA capable and device is not sleep STA + * capable, then teardown TDLS link when scan is initiated. If peer + * is buffer STA and we can be sleep STA then TDLS link is maintained + * during scan. + * 1: Maintain TDLS link and allow scan even if peer is not buffer STA + * capable and device is not sleep STA capable. There will be loss of + * Rx pkts since peer would not know when device moves away from tdls + * channel. Tx on TDLS link would stop when device moves away from tdls + * channel. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_SCAN_ENABLE CFG_INI_BOOL( \ + "gEnableTDLSScan", \ + 0, \ + "Allow scan and maintain TDLS link") + +/* + * + * gTDLSPeerKickoutThreshold - TDLS peer kick out threshold to firmware. + * @Min: 10 + * @Max: 5000 + * @Default: 96 + * + * This ini is used to configure TDLS peer kick out threshold to firmware. + * Firmware will use this value to determine, when to send TDLS + * peer kick out event to host. + * E.g. + * if peer kick out threshold is 10, then firmware will wait for 10 + * consecutive packet failures and then send TDLS kick out + * notification to host driver + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_PEER_KICKOUT_THRESHOLD CFG_INI_UINT( \ + "gTDLSPeerKickoutThreshold", \ + 10, \ + 5000, \ + 96, \ + CFG_VALUE_OR_DEFAULT, \ + "TDLS peer kick out threshold to firmware") + +/* + * + * gTDLSEnableDeferTime - Timer to defer for enabling TDLS on P2P listen. + * @Min: 500 + * @Max: 6000 + * @Default: 2000 + * + * This ini is used to set the timer to defer for enabling TDLS on P2P + * listen (value in milliseconds). + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_ENABLE_DEFER_TIMER CFG_INI_UINT( \ + "gTDLSEnableDeferTime", \ + 500, \ + 6000, \ + 2000, \ + CFG_VALUE_OR_DEFAULT, \ + "Timer to defer for enabling TDLS on P2P listen") + +/* + * + * DelayedTriggerFrmInt - delayed trigger frame interval. + * @Min: 500 + * @Max: 6000 + * @Default: 2000 + * + * This ini is used to set the delayed trigger frame interval. + * + * Related: gEnableTDLSSupport. + * + * Supported Feature: TDLS + * + * Usage: Internal/External + * + * + */ +#define CFG_TDLS_DELAYED_TRGR_FRM_INT CFG_INI_UINT( \ + "DelayedTriggerFrmInt", \ + 1, \ + 4294967295UL, \ + 3000, \ + CFG_VALUE_OR_DEFAULT, \ + "delayed trigger frame interval") + +#define CFG_TDLS_ALL \ + CFG(CFG_TDLS_QOS_WMM_UAPSD_MASK) \ + CFG(CFG_TDLS_BUF_STA_ENABLED) \ + CFG(CFG_TDLS_PUAPSD_INACT_TIME) \ + CFG(CFG_TDLS_RX_FRAME_THRESHOLD) \ + CFG(CFG_TDLS_OFF_CHANNEL_ENABLED) \ + CFG(CFG_TDLS_SUPPORT_ENABLE) \ + CFG(CFG_TDLS_IMPLICIT_TRIGGER) \ + CFG(CFG_TDLS_TX_STATS_PERIOD) \ + CFG(CFG_TDLS_TX_PACKET_THRESHOLD) \ + CFG(CFG_TDLS_MAX_DISCOVERY_ATTEMPT) \ + CFG(CFG_TDLS_IDLE_TIMEOUT) \ + CFG(CFG_TDLS_IDLE_PACKET_THRESHOLD) \ + CFG(CFG_TDLS_RSSI_TRIGGER_THRESHOLD) \ + CFG(CFG_TDLS_RSSI_TEARDOWN_THRESHOLD) \ + CFG(CFG_TDLS_RSSI_DELTA) \ + CFG(CFG_TDLS_PREFERRED_OFF_CHANNEL_NUM) \ + CFG(CFG_TDLS_PREFERRED_OFF_CHANNEL_BW) \ + CFG(CFG_TDLS_PUAPSD_INACTIVITY_TIME) \ + CFG(CFG_TDLS_PUAPSD_RX_FRAME_THRESHOLD) \ + CFG(CFG_TDLS_PUAPSD_PEER_TRAFFIC_IND_WINDOW) \ + CFG(CFG_TDLS_PUAPSD_PEER_TRAFFIC_RSP_TIMEOUT) \ + CFG(CFG_TDLS_EXTERNAL_CONTROL) \ + CFG(CFG_TDLS_WMM_MODE_ENABLE) \ + CFG(CFG_TDLS_SCAN_ENABLE) \ + CFG(CFG_TDLS_PEER_KICKOUT_THRESHOLD) \ + CFG(CFG_TDLS_ENABLE_DEFER_TIMER) \ + CFG(CFG_TDLS_DELAYED_TRGR_FRM_INT) + +#endif diff --git a/components/tdls/dispatcher/inc/wlan_tdls_cfg_api.h b/components/tdls/dispatcher/inc/wlan_tdls_cfg_api.h new file mode 100644 index 000000000000..6229c0ca1bd3 --- /dev/null +++ b/components/tdls/dispatcher/inc/wlan_tdls_cfg_api.h @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: Contains p2p configures interface definitions + */ + +#ifndef _WLAN_TDLS_CFG_API_H_ +#define _WLAN_TDLS_CFG_API_H_ + +#include + +struct wlan_objmgr_psoc; + +/** + * cfg_tdls_get_support_enable() - get tdls support enable + * @psoc: pointer to psoc object + * @val: pointer to tdls support enable/disable + * + * This function gets tdls support enable + */ +QDF_STATUS +cfg_tdls_get_support_enable(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * cfg_tdls_set_support_enable() - set tdls support enable + * @psoc: pointer to psoc object + * @val: set tdls support enable/disable + * + * This function sets tdls support enable + */ +QDF_STATUS +cfg_tdls_set_support_enable(struct wlan_objmgr_psoc *psoc, + bool val); + +/** + * cfg_tdls_get_external_control() - get tdls external control + * @psoc: pointer to psoc object + * @val: pointer to tdls external control enable/disable + * + * This function gets tdls external control + */ +QDF_STATUS +cfg_tdls_get_external_control(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * cfg_tdls_get_uapsd_mask() - get tdls uapsd mask + * @psoc: pointer to psoc object + * @val: pointer to tdls uapsd mask + * + * This function gets tdls uapsd mask + */ +QDF_STATUS +cfg_tdls_get_uapsd_mask(struct wlan_objmgr_psoc *psoc, + uint32_t *val); + +/** + * cfg_tdls_get_buffer_sta_enable() - get tdls buffer sta enable + * @psoc: pointer to psoc object + * @val: pointer to tdls buffer sta enable + * + * This function gets tdls buffer sta enable + */ +QDF_STATUS +cfg_tdls_get_buffer_sta_enable(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * cfg_tdls_set_buffer_sta_enable() - set tdls buffer sta enable + * @psoc: pointer to psoc object + * @val: tdls buffer sta enable + * + * This function sets tdls buffer sta enable + */ +QDF_STATUS +cfg_tdls_set_buffer_sta_enable(struct wlan_objmgr_psoc *psoc, + bool val); + +/** + * cfg_tdls_get_uapsd_inactivity_time() - get tdls uapsd inactivity time + * @psoc: pointer to psoc object + * @val: pointer to tdls uapsd inactivity time + * + * This function gets tdls uapsd inactivity time + */ +QDF_STATUS +cfg_tdls_get_uapsd_inactivity_time(struct wlan_objmgr_psoc *psoc, + uint32_t *val); + +/** + * cfg_tdls_get_rx_pkt_threshold() - get tdls rx pkt threshold + * @psoc: pointer to psoc object + * @val: pointer to tdls tdls rx pkt threshold + * + * This function gets tdls rx pkt threshold + */ +QDF_STATUS +cfg_tdls_get_rx_pkt_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t *val); + +/** + * cfg_tdls_get_off_channel_enable() - get tdls off channel enable + * @psoc: pointer to psoc object + * @val: pointer to tdls off channel enable + * + * This function gets tdls off channel enable + */ +QDF_STATUS +cfg_tdls_get_off_channel_enable(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * cfg_tdls_set_off_channel_enable() - set tdls off channel enable + * @psoc: pointer to psoc object + * @val: tdls off channel enable + * + * This function sets tdls off channel enable + */ +QDF_STATUS +cfg_tdls_set_off_channel_enable(struct wlan_objmgr_psoc *psoc, + bool val); + +/** + * cfg_tdls_get_wmm_mode_enable() - get tdls wmm mode enable + * @psoc: pointer to psoc object + * @val: pointer to tdls wmm mode enable + * + * This function gets tdls wmm mode enable + */ +QDF_STATUS +cfg_tdls_get_wmm_mode_enable(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * cfg_tdls_set_vdev_nss_2g() - set tdls vdev nss 2g + * @psoc: pointer to psoc object + * @val: tdls vdev nss 2g + * + * This function sets tdls vdev nss 2g + */ +QDF_STATUS +cfg_tdls_set_vdev_nss_2g(struct wlan_objmgr_psoc *psoc, + uint8_t val); + +/** + * cfg_tdls_set_vdev_nss_5g() - set tdls vdev nss 5g + * @psoc: pointer to psoc object + * @val: tdls vdev nss 5g + * + * This function sets tdls vdev nss 5g + */ +QDF_STATUS +cfg_tdls_set_vdev_nss_5g(struct wlan_objmgr_psoc *psoc, + uint8_t val); + +/** + * cfg_tdls_get_sleep_sta_enable() - get tdls sleep sta enable + * @psoc: pointer to psoc object + * @val: pointer to tdls sleep sta enable + * + * This function gets tdls sleep sta enable + */ +QDF_STATUS +cfg_tdls_get_sleep_sta_enable(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * cfg_tdls_set_sleep_sta_enable() - set tdls sleep sta enable + * @psoc: pointer to psoc object + * @val: tdls sleep sta enable + * + * This function sets tdls sleep sta enable + */ +QDF_STATUS +cfg_tdls_set_sleep_sta_enable(struct wlan_objmgr_psoc *psoc, + bool val); + +/** + * cfg_tdls_get_scan_enable() - get tdls scan enable + * @psoc: pointer to psoc object + * @val: pointer to tdls scan enable + * + * This function gets tdls scan enable + */ +QDF_STATUS +cfg_tdls_get_scan_enable(struct wlan_objmgr_psoc *psoc, + bool *val); + +/** + * cfg_tdls_set_scan_enable() - set tdls scan enable + * @psoc: pointer to psoc object + * @val: tdls scan enable + * + * This function sets tdls scan enable + */ +QDF_STATUS +cfg_tdls_set_scan_enable(struct wlan_objmgr_psoc *psoc, + bool val); + +#endif /* _WLAN_TDLS_CFG_API_H_ */ diff --git a/components/tdls/dispatcher/inc/wlan_tdls_public_structs.h b/components/tdls/dispatcher/inc/wlan_tdls_public_structs.h new file mode 100644 index 000000000000..07bc155d9591 --- /dev/null +++ b/components/tdls/dispatcher/inc/wlan_tdls_public_structs.h @@ -0,0 +1,1169 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_public_structs.h + * + * TDLS public structure definations + */ + +#ifndef _WLAN_TDLS_STRUCTS_H_ +#define _WLAN_TDLS_STRUCTS_H_ +#include +#include +#include +#include +#include + + +#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_MAC_WMI_MAX_SUPP_CHANNELS 128 +#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 + +#define AC_PRIORITY_NUM 4 + +/* default tdls serialize timeout is set to 10 secs */ +#define TDLS_DEFAULT_SERIALIZE_CMD_TIMEOUT 10000 + +/** Maximum time(ms) to wait for tdls add sta to complete **/ +#define WAIT_TIME_TDLS_ADD_STA (TDLS_DEFAULT_SERIALIZE_CMD_TIMEOUT + 1000) + +/** Maximum time(ms) to wait for tdls del sta to complete **/ +#define WAIT_TIME_TDLS_DEL_STA (TDLS_DEFAULT_SERIALIZE_CMD_TIMEOUT + 1000) + +/** 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_FOR_TDLS_MGMT 11000 + +/** Maximum time(ms) to wait for tdls mgmt to complete **/ +#define WAIT_TIME_FOR_TDLS_USER_CMD 11000 + +/** Maximum waittime for TDLS teardown links **/ +#define WAIT_TIME_FOR_TDLS_TEARDOWN_LINKS 10000 + +/** Maximum waittime for TDLS antenna switch **/ +#define WAIT_TIME_FOR_TDLS_ANTENNA_SWITCH 1000 + +#define TDLS_TEARDOWN_PEER_UNREACHABLE 25 +#define TDLS_TEARDOWN_PEER_UNSPEC_REASON 26 + +#define INVALID_TDLS_PEER_ID 0xFF +#define TDLS_STA_INDEX_CHECK(sta_id) \ + (((sta_id) >= 0) && ((sta_id) < 0xFF)) + +/** + * 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 +}; + +/** + * enum tdls_conc_cap - tdls concurrency support + * @TDLS_SUPPORTED_ONLY_ON_STA: only support sta tdls + * @TDLS_SUPPORTED_ONLY_ON_P2P_CLIENT: only support p2p client tdls + */ +enum tdls_conc_cap { + TDLS_SUPPORTED_ONLY_ON_STA = 0, + TDLS_SUPPORTED_ONLY_ON_P2P_CLIENT, +}; + +/** + * enum tdls_peer_capab - tdls capability type + * @TDLS_CAP_NOT_SUPPORTED: tdls not supported + * @TDLS_CAP_UNKNOWN: unknown capability + * @TDLS_CAP_SUPPORTED: tdls capability supported + */ +enum tdls_peer_capab { + TDLS_CAP_NOT_SUPPORTED = -1, + TDLS_CAP_UNKNOWN = 0, + TDLS_CAP_SUPPORTED = 1, +}; + +/** + * 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 + * @TDLS_LINK_CONNECTING: tdls link connecting + * @TDLS_LINK_CONNECTED: tdls link connected + * @TDLS_LINK_TEARING: tdls link tearing + */ +enum tdls_link_state { + TDLS_LINK_IDLE = 0, + TDLS_LINK_DISCOVERING, + TDLS_LINK_DISCOVERED, + TDLS_LINK_CONNECTING, + TDLS_LINK_CONNECTED, + TDLS_LINK_TEARING, +}; + +/** + * 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 + * @TDLS_LINK_UNSUPPORTED_BAND: Remote side doesn't support this band + * @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_state_reason { + TDLS_LINK_SUCCESS, + TDLS_LINK_UNSPECIFIED = -1, + TDLS_LINK_NOT_SUPPORTED = -2, + TDLS_LINK_UNSUPPORTED_BAND = -3, + TDLS_LINK_NOT_BENEFICIAL = -4, + TDLS_LINK_DROPPED_BY_REMOTE = -5, +}; + +/** + * 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 + * @TDLS_SUPPORT_EXP_TRIG_ONLY: Explicit trigger mode + * @TDLS_SUPPORT_IMP_MODE: Implicit mode + * @TDLS_SUPPORT_EXT_CONTROL: External control mode + */ +enum tdls_feature_mode { + TDLS_SUPPORT_DISABLED = 0, + TDLS_SUPPORT_SUSPENDED, + TDLS_SUPPORT_EXP_TRIG_ONLY, + TDLS_SUPPORT_IMP_MODE, + TDLS_SUPPORT_EXT_CONTROL, +}; + +/** + * enum tdls_command_type - TDLS command type + * @TDLS_CMD_TX_ACTION: send tdls action frame + * @TDLS_CMD_ADD_STA: add tdls peer + * @TDLS_CMD_CHANGE_STA: change tdls peer + * @TDLS_CMD_ENABLE_LINK: enable tdls link + * @TDLS_CMD_DISABLE_LINK: disable tdls link + * @TDLS_CMD_CONFIG_FORCE_PEER: config external peer + * @TDLS_CMD_REMOVE_FORCE_PEER: remove external peer + * @TDLS_CMD_STATS_UPDATE: update tdls stats + * @TDLS_CMD_CONFIG_UPDATE: config tdls + * @TDLS_CMD_SCAN_DONE: scon done event + * @TDLS_CMD_SET_RESPONDER: responder event + * @TDLS_NOTIFY_STA_CONNECTION: notify sta connection + * @TDLS_NOTIFY_STA_DISCONNECTION: notify sta disconnection + * @TDLS_CMD_SET_TDLS_MODE: set the tdls mode + * @TDLS_CMD_SESSION_INCREMENT: notify session increment + * @TDLS_CMD_SESSION_DECREMENT: notify session decrement + * @TDLS_CMD_TEARDOWN_LINKS: notify teardown + * @TDLS_NOTIFY_RESET_ADAPTERS: notify adapter reset + * @TDLS_CMD_GET_ALL_PEERS: get all the tdls peers from the list + * @TDLS_CMD_ANTENNA_SWITCH: dynamic tdls antenna switch + * @TDLS_CMD_SET_OFFCHANNEL: tdls offchannel + * @TDLS_CMD_SET_OFFCHANMODE: tdls offchannel mode + * @TDLS_CMD_SET_SECOFFCHANOFFSET: tdls secondary offchannel offset + * @TDLS_DELETE_ALL_PEERS_INDICATION: tdls delete all peers indication + */ +enum tdls_command_type { + TDLS_CMD_TX_ACTION = 1, + TDLS_CMD_ADD_STA, + TDLS_CMD_CHANGE_STA, + TDLS_CMD_ENABLE_LINK, + TDLS_CMD_DISABLE_LINK, + TDLS_CMD_CONFIG_FORCE_PEER, + TDLS_CMD_REMOVE_FORCE_PEER, + TDLS_CMD_STATS_UPDATE, + TDLS_CMD_CONFIG_UPDATE, + TDLS_CMD_SCAN_DONE, + TDLS_CMD_SET_RESPONDER, + TDLS_NOTIFY_STA_CONNECTION, + TDLS_NOTIFY_STA_DISCONNECTION, + TDLS_CMD_SET_TDLS_MODE, + TDLS_CMD_SESSION_INCREMENT, + TDLS_CMD_SESSION_DECREMENT, + TDLS_CMD_TEARDOWN_LINKS, + TDLS_NOTIFY_RESET_ADAPTERS, + TDLS_CMD_GET_ALL_PEERS, + TDLS_CMD_ANTENNA_SWITCH, + TDLS_CMD_SET_OFFCHANNEL, + TDLS_CMD_SET_OFFCHANMODE, + TDLS_CMD_SET_SECOFFCHANOFFSET, + TDLS_DELETE_ALL_PEERS_INDICATION +}; + +/** + * enum tdls_event_type - TDLS event type + * @TDLS_EVENT_VDEV_STATE_CHANGE: umac connect/disconnect event + * @TDLS_EVENT_MGMT_TX_ACK_CNF: tx tdls frame ack event + * @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 + * @TDLS_EVENT_TEARDOWN_LINKS_DONE: teardown completion event + * @TDLS_EVENT_USER_CMD: tdls user command + * @TDLS_EVENT_ANTENNA_SWITCH: antenna switch event + */ +enum tdls_event_type { + TDLS_EVENT_VDEV_STATE_CHANGE = 0, + TDLS_EVENT_MGMT_TX_ACK_CNF, + TDLS_EVENT_RX_MGMT, + TDLS_EVENT_ADD_PEER, + TDLS_EVENT_DEL_PEER, + TDLS_EVENT_DISCOVERY_REQ, + TDLS_EVENT_TEARDOWN_REQ, + TDLS_EVENT_SETUP_REQ, + TDLS_EVENT_TEARDOWN_LINKS_DONE, + TDLS_EVENT_USER_CMD, + TDLS_EVENT_ANTENNA_SWITCH, +}; + +/** + * 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 + * @QCA_WIFI_HAL_TDLS_ESTABLISHED_OFF_CHANNEL: Direct link established using MCC + * @QCA_WIFI_HAL_TDLS_DROPPED: Direct link was established, but is now dropped + * @QCA_WIFI_HAL_TDLS_FAILED: Direct link failed + */ +enum tdls_state_t { + QCA_WIFI_HAL_TDLS_S_DISABLED = 1, + QCA_WIFI_HAL_TDLS_S_ENABLED, + QCA_WIFI_HAL_TDLS_S_ESTABLISHED, + QCA_WIFI_HAL_TDLS_S_ESTABLISHED_OFF_CHANNEL, + QCA_WIFI_HAL_TDLS_S_DROPPED, + 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 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: TDLS/BT role change notification for + * connection tracker + */ +enum tdls_event_msg_type { + TDLS_SHOULD_DISCOVER = 0, + TDLS_SHOULD_TEARDOWN, + TDLS_PEER_DISCONNECTED, + TDLS_CONNECTION_TRACKER_NOTIFY +}; + +/** + * enum tdls_event_reason - TDLS event reason + * @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, + 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, + TDLS_SCAN_STARTED, + TDLS_SCAN_COMPLETED, +}; + +/** + * enum tdls_disable_sources - TDLS disable sources + * @TDLS_SET_MODE_SOURCE_USER: disable from user + * @TDLS_SET_MODE_SOURCE_SCAN: disable during scan + * @TDLS_SET_MODE_SOURCE_OFFCHANNEL: disable during offchannel + * @TDLS_SET_MODE_SOURCE_BTC: disable during bluetooth + * @TDLS_SET_MODE_SOURCE_P2P: disable during p2p + */ +enum tdls_disable_sources { + TDLS_SET_MODE_SOURCE_USER = 0, + TDLS_SET_MODE_SOURCE_SCAN, + TDLS_SET_MODE_SOURCE_OFFCHANNEL, + TDLS_SET_MODE_SOURCE_BTC, + TDLS_SET_MODE_SOURCE_P2P, +}; + +/** + * 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 + * @buf_len: buffer length + * @tx_timer: tx send timer + */ +struct tx_frame { + uint8_t *buf; + size_t buf_len; + qdf_timer_t tx_timer; +}; + +/** + * enum tdls_feature_bit + * @TDLS_FEATURE_OFF_CHANNEL: tdls off channel + * @TDLS_FEATURE_WMM: tdls wmm + * @TDLS_FEATURE_BUFFER_STA: tdls buffer sta + * @TDLS_FEATURE_SLEEP_STA: tdls sleep sta feature + * @TDLS_FEATURE_SCAN: tdls scan + * @TDLS_FEATURE_ENABLE: tdls enabled + * @TDLS_FEAUTRE_IMPLICIT_TRIGGER: tdls implicit trigger + * @TDLS_FEATURE_EXTERNAL_CONTROL: tdls external control + */ +enum tdls_feature_bit { + TDLS_FEATURE_OFF_CHANNEL, + TDLS_FEATURE_WMM, + TDLS_FEATURE_BUFFER_STA, + TDLS_FEATURE_SLEEP_STA, + TDLS_FEATURE_SCAN, + TDLS_FEATURE_ENABLE, + TDLS_FEAUTRE_IMPLICIT_TRIGGER, + TDLS_FEATURE_EXTERNAL_CONTROL +}; + +#define TDLS_IS_OFF_CHANNEL_ENABLED(flags) \ + CHECK_BIT(flags, TDLS_FEATURE_OFF_CHANNEL) +#define TDLS_IS_WMM_ENABLED(flags) \ + CHECK_BIT(flags, TDLS_FEATURE_WMM) +#define TDLS_IS_BUFFER_STA_ENABLED(flags) \ + CHECK_BIT(flags, TDLS_FEATURE_BUFFER_STA) +#define TDLS_IS_SLEEP_STA_ENABLED(flags) \ + CHECK_BIT(flags, TDLS_FEATURE_SLEEP_STA) +#define TDLS_IS_SCAN_ENABLED(flags) \ + CHECK_BIT(flags, TDLS_FEATURE_SCAN) +#define TDLS_IS_ENABLED(flags) \ + CHECK_BIT(flags, TDLS_FEATURE_ENABLE) +#define TDLS_IS_IMPLICIT_TRIG_ENABLED(flags) \ + CHECK_BIT(flags, TDLS_FEAUTRE_IMPLICIT_TRIGGER) +#define TDLS_IS_EXTERNAL_CONTROL_ENABLED(flags) \ + CHECK_BIT(flags, TDLS_FEATURE_EXTERNAL_CONTROL) + +/** + * struct tdls_user_config - TDLS user configuration + * @tdls_tx_states_period: tdls tx states period + * @tdls_tx_pkt_threshold: tdls tx packets threshold + * @tdls_rx_pkt_threshold: tdls rx packets threshold + * @tdls_max_discovery_attempt: tdls discovery max times + * @tdls_idle_timeout: tdls idle timeout + * @tdls_idle_pkt_threshold: tdls idle packets threshold + * @tdls_rssi_trigger_threshold: tdls rssi trigger threshold + * @tdls_rssi_teardown_threshold: tdls rssi tear down threshold + * @tdls_rssi_delta: tdls rssi delta + * @tdls_uapsd_mask: tdls uapsd mask + * @tdls_uapsd_inactivity_time: tdls uapsd inactivity time + * @tdls_uapsd_pti_window: tdls peer traffic indication window + * @tdls_uapsd_ptr_timeout: tdls peer response timeout + * @tdls_feature_flags: tdls feature flags + * @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 + * @tdls_vdev_nss_2g: tdls NSS setting for 2G band + * @tdls_vdev_nss_5g: tdls NSS setting for 5G band + * @tdls_buffer_sta_enable: tdls buffer station enable + * @tdls_off_chan_enable: tdls off channel enable + * @tdls_wmm_mode_enable: tdls wmm mode enable + * @tdls_external_control: tdls external control enable + * @tdls_implicit_trigger_enable: tdls implicit trigger enable + * @tdls_scan_enable: tdls scan enable + * @tdls_sleep_sta_enable: tdls sleep sta enable + * @tdls_support_enable: tdls support enable + */ +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_timeout; + uint32_t tdls_idle_pkt_threshold; + int32_t tdls_rssi_trigger_threshold; + int32_t tdls_rssi_teardown_threshold; + uint32_t tdls_rssi_delta; + uint32_t tdls_uapsd_mask; + uint32_t tdls_uapsd_inactivity_time; + uint32_t tdls_uapsd_pti_window; + uint32_t tdls_uapsd_ptr_timeout; + uint32_t tdls_feature_flags; + 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; + uint8_t tdls_vdev_nss_2g; + uint8_t tdls_vdev_nss_5g; + bool tdls_buffer_sta_enable; + bool tdls_off_chan_enable; + bool tdls_wmm_mode_enable; + bool tdls_external_control; + bool tdls_implicit_trigger_enable; + bool tdls_scan_enable; + bool tdls_sleep_sta_enable; + bool tdls_support_enable; +}; + +/** + * struct tdls_config_params - tdls configure paramets + * @tdls: tdls support mode + * @tx_period_t: tdls tx stats period + * @tx_packet_n: tdls tx packets number threshold + * @discovery_tries_n: tdls max discovery attempt count + * @idle_timeout_t: tdls idle time timeout + * @idle_packet_n: tdls idle pkt threshold + * @rssi_trigger_threshold: tdls rssi trigger threshold, checked before setup + * @rssi_teardown_threshold: tdls rssi teardown threshold + * @rssi_delta: rssi delta + */ +struct tdls_config_params { + uint32_t tdls; + uint32_t tx_period_t; + uint32_t tx_packet_n; + uint32_t discovery_tries_n; + uint32_t idle_timeout_t; + uint32_t idle_packet_n; + int32_t rssi_trigger_threshold; + int32_t rssi_teardown_threshold; + int32_t rssi_delta; +}; + +/** + * struct tdls_tx_cnf: tdls tx ack + * @vdev_id: vdev id + * @action_cookie: frame cookie + * @buf: frame buf + * @buf_len: buffer length + * @status: tx send status + */ +struct tdls_tx_cnf { + int vdev_id; + uint64_t action_cookie; + void *buf; + size_t buf_len; + int status; +}; + +/** + * struct tdls_rx_mgmt_frame - rx mgmt frame structure + * @frame_len: frame length + * @rx_chan: rx channel + * @vdev_id: vdev id + * @frm_type: frame type + * @rx_rssi: rx rssi + * @buf: buffer address + */ +struct tdls_rx_mgmt_frame { + uint32_t frame_len; + uint32_t rx_chan; + uint32_t vdev_id; + uint32_t frm_type; + uint32_t rx_rssi; + uint8_t buf[1]; +}; + +/** + * tdls_rx_callback() - Callback for rx mgmt frame + * @user_data: user data associated to this rx mgmt frame. + * @rx_frame: RX mgmt frame + * + * This callback will be used to give rx frames to hdd. + * + * Return: None + */ +typedef void (*tdls_rx_callback)(void *user_data, + struct tdls_rx_mgmt_frame *rx_frame); + +/** + * tdls_wmm_check() - Callback for wmm info + * @psoc: psoc object + * + * This callback will be used to check wmm information + * + * Return: true or false + */ +typedef bool (*tdls_wmm_check)(uint8_t vdev_id); + + +/* 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); + +/* This callback is used to report events to os_if layer */ +typedef void (*tdls_evt_callback) (void *data, + enum tdls_event_type ev_type, + struct tdls_osif_indication *event); + +/* This callback is used to register TDLS peer with the datapath */ +typedef QDF_STATUS (*tdls_register_peer_callback)(void *userdata, + uint32_t vdev_id, + const uint8_t *mac, + uint16_t stat_id, + uint8_t qos); + +/* This callback is used to deregister TDLS peer from the datapath */ +typedef QDF_STATUS (*tdls_deregister_peer_callback)(void *userdata, + uint32_t vdev_id, + uint8_t sta_id); + +/* This callback is used to update datapath vdev flags */ +typedef QDF_STATUS +(*tdls_dp_vdev_update_flags_callback)(void *cbk_data, + uint8_t sta_id, + uint32_t vdev_param, + bool is_link_up); + +/* This callback is to release vdev ref for tdls offchan param related msg */ +typedef void (*tdls_offchan_parms_callback)(struct wlan_objmgr_vdev *vdev); + +/** + * 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_del_all_peers: pass eWNI_SME_DEL_ALL_TDLS_PEERS + * @tdls_update_dp_vdev_flags: pass CDP_UPDATE_TDLS_FLAGS + * @tdls_event_cb: tdls event callback + * @tdls_evt_cb_data: tdls event data + * @tdls_peer_context: userdata for register/deregister TDLS peer + * @tdls_reg_peer: register tdls peer with datapath + * @tdls_dereg_peer: deregister tdls peer from datapath + * @tdls_dp_vdev_update: update vdev flags in datapath + */ +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; + uint16_t tdls_del_all_peers; + uint32_t tdls_update_dp_vdev_flags; + tdls_rx_callback tdls_rx_cb; + void *tdls_rx_cb_data; + tdls_wmm_check tdls_wmm_cb; + void *tdls_wmm_cb_data; + tdls_evt_callback tdls_event_cb; + void *tdls_evt_cb_data; + void *tdls_peer_context; + tdls_register_peer_callback tdls_reg_peer; + tdls_deregister_peer_callback tdls_dereg_peer; + tdls_dp_vdev_update_flags_callback tdls_dp_vdev_update; +}; + +/** + * struct tdls_add_peer_params - add peer request parameter + * @peer_addr: peer mac addr + * @peer_type: peer type + * @vdev_id: vdev id + */ +struct tdls_add_peer_params { + uint8_t peer_addr[QDF_MAC_ADDR_SIZE]; + uint32_t peer_type; + uint32_t vdev_id; +}; + +/** + * struct tdls_add_peer_request - peer add request + * @vdev: vdev + * @add_peer_req: add peer request parameters + */ +struct tdls_add_peer_request { + struct wlan_objmgr_vdev *vdev; + struct tdls_add_peer_params add_peer_req; +}; + +/** + * struct tdls_del_peer_params - delete peer request parameter + * @peer_addr: peer mac addr + * @peer_type: peer type + * @vdev_id: vdev id + */ +struct tdls_del_peer_params { + const uint8_t *peer_addr; + uint32_t peer_type; + uint32_t vdev_id; +}; + +/** + * struct tdls_del_peer_request - peer delete request + * @vdev: vdev + * @del_peer_req: delete peer request parameters + */ +struct tdls_del_peer_request { + struct wlan_objmgr_vdev *vdev; + struct tdls_del_peer_params del_peer_req; +}; + +/** + * struct vhgmcsinfo - VHT MCS information + * @rx_mcs_map: RX MCS map 2 bits for each stream, total 8 streams + * @rx_highest: Indicates highest long GI VHT PPDU data rate + * STA can receive. Rate expressed in units of 1 Mbps. + * If this field is 0 this value should not be used to + * consider the highest RX data rate supported. + * @tx_mcs_map: TX MCS map 2 bits for each stream, total 8 streams + * @tx_highest: Indicates highest long GI VHT PPDU data rate + * STA can transmit. Rate expressed in units of 1 Mbps. + * If this field is 0 this value should not be used to + * consider the highest TX data rate supported. + */ +struct vhtmcsinfo { + uint16_t rx_mcs_map; + uint16_t rx_highest; + uint16_t tx_mcs_map; + uint16_t tx_highest; +}; + +/** + * struct vhtcap - VHT capabilities + * + * This structure is the "VHT capabilities element" as + * described in 802.11ac D3.0 8.4.2.160 + * @vht_cap_info: VHT capability info + * @supp_mcs: VHT MCS supported rates + */ +struct vhtcap { + uint32_t vht_capinfo; + struct vhtmcsinfo supp_mcs; +}; + +struct tdls_update_peer_params { + uint8_t peer_addr[QDF_MAC_ADDR_SIZE]; + uint32_t peer_type; + uint32_t vdev_id; + uint16_t capability; + uint8_t extn_capability[WLAN_MAC_MAX_EXTN_CAP]; + uint8_t supported_rates_len; + 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; + uint8_t supported_channels_len; + 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 { + struct wlan_objmgr_vdev *vdev; + 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 + * + * @vdev_id: vdev id + * @tdls_state: tdls state + * @notification_interval_ms: notification interval in ms + * @tx_discovery_threshold: tx discovery threshold + * @tx_teardown_threshold: tx teardown threshold + * @rssi_teardown_threshold: rx teardown threshold + * @rssi_delta: rssi delta + * @tdls_options: tdls options + * @peer_traffic_ind_window: peer traffic indication window + * @peer_traffic_response_timeout: peer traffic response timeout + * @puapsd_mask: puapsd mask + * @puapsd_inactivity_time: puapsd inactivity time + * @puapsd_rx_frame_threshold: puapsd rx frame threshold + * @teardown_notification_ms: tdls teardown notification interval + * @tdls_peer_kickout_threshold: tdls packets threshold + * for peer kickout operation + */ +struct tdls_info { + uint32_t vdev_id; + uint32_t tdls_state; + uint32_t notification_interval_ms; + uint32_t tx_discovery_threshold; + uint32_t tx_teardown_threshold; + int32_t rssi_teardown_threshold; + int32_t rssi_delta; + uint32_t tdls_options; + uint32_t peer_traffic_ind_window; + uint32_t peer_traffic_response_timeout; + uint32_t puapsd_mask; + uint32_t puapsd_inactivity_time; + uint32_t puapsd_rx_frame_threshold; + uint32_t teardown_notification_ms; + uint32_t tdls_peer_kickout_threshold; +}; + +/** + * struct tdls_ch_params - channel parameters + * @chan_id: ID of the channel + * @pwr: power level + * @dfs_set: is dfs supported or not + * @half_rate: is the channel operating at 10MHz + * @quarter_rate: is the channel operating at 5MHz + */ +struct tdls_ch_params { + uint8_t chan_id; + uint8_t pwr; + bool dfs_set; + bool half_rate; + bool quarter_rate; +}; + +/** + * struct tdls_peer_params - TDLS peer capablities parameters + * @is_peer_responder: is peer responder or not + * @peer_uapsd_queue: peer uapsd queue + * @peer_max_sp: peer max SP value + * @peer_buff_sta_support: peer buffer sta supported or not + * @peer_off_chan_support: peer offchannel support + * @peer_curr_operclass: peer current operating class + * @self_curr_operclass: self current operating class + * @peer_chanlen: peer channel length + * @peer_chan: peer channel list + * @peer_oper_classlen: peer operating class length + * @peer_oper_class: peer operating class + * @pref_off_channum: peer offchannel number + * @pref_off_chan_bandwidth: peer offchannel bandwidth + * @opclass_for_prefoffchan: operating class for offchannel + */ +struct tdls_peer_params { + uint8_t is_peer_responder; + uint8_t peer_uapsd_queue; + uint8_t peer_max_sp; + uint8_t peer_buff_sta_support; + uint8_t peer_off_chan_support; + uint8_t peer_curr_operclass; + uint8_t self_curr_operclass; + uint8_t peer_chanlen; + struct tdls_ch_params peer_chan[WLAN_MAC_WMI_MAX_SUPP_CHANNELS]; + uint8_t peer_oper_classlen; + uint8_t peer_oper_class[WLAN_MAX_SUPP_OPER_CLASSES]; + uint8_t pref_off_channum; + uint8_t pref_off_chan_bandwidth; + uint8_t opclass_for_prefoffchan; +}; + +/** + * 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_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_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 + * @tdls_off_ch: Target Off Channel + * @oper_class: Operating class for target channel + * @is_responder: Responder or initiator + */ +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; + uint8_t tdls_off_ch; + uint8_t tdls_sw_mode; + uint8_t oper_class; + uint8_t is_responder; +}; + +/** + * enum uapsd_access_cat - U-APSD Access Categories + * @UAPSD_AC_BE: best effort + * @UAPSD_AC_BK: back ground + * @UAPSD_AC_VI: video + * @UAPSD_AC_VO: voice + */ +enum uapsd_access_cat { + UAPSD_AC_BE, + UAPSD_AC_BK, + UAPSD_AC_VI, + UAPSD_AC_VO +}; + +/** + * enum tspec_dir_type - TSPEC Direction type + * @TX_DIR: uplink + * @RX_DIR: downlink + * @BI_DIR: bidirectional + */ +enum tspec_dir_type { + TX_DIR = 0, + RX_DIR = 1, + BI_DIR = 2, +}; + +/** + * struct sta_uapsd_params - uapsd auto trig params + * @wmm_ac: WMM access category from 0 to 3 + * @user_priority: User priority to use in trigger frames + * @service_interval: service interval + * @suspend_interval: suspend interval + * @delay_interval: delay interval + */ +struct sta_uapsd_params { + uint32_t wmm_ac; + uint32_t user_priority; + uint32_t service_interval; + uint32_t suspend_interval; + uint32_t delay_interval; +}; + +/** + * struct sta_uapsd_trig_params - uapsd trigger parameter + * @vdevid: vdev id + * @peer_addr: peer address + * @auto_triggerparam: trigger parameters + * @num_ac: no of access category + */ +struct sta_uapsd_trig_params { + uint32_t vdevid; + uint8_t peer_addr[QDF_MAC_ADDR_SIZE]; + struct sta_uapsd_params *auto_triggerparam; + uint32_t num_ac; +}; + +/** + * struct tdls_event_info - firmware tdls event + * @vdev_id: vdev id + * @peermac: peer mac address + * @message_type: message type + * @peer_reason: reason + */ +struct tdls_event_info { + uint8_t vdev_id; + struct qdf_mac_addr peermac; + uint16_t message_type; + uint32_t peer_reason; +}; + +/** + * struct tdls_event_notify - tdls event notify + * @vdev: vdev object + * @event: tdls event + */ +struct tdls_event_notify { + struct wlan_objmgr_vdev *vdev; + struct tdls_event_info event; +}; + +/** + * struct tdls_event_notify - tdls event notify + * @peer_mac: peer's mac address + * @frame_type: Type of TDLS mgmt frame to be sent + * @dialog: dialog token used in the frame. + * @status_code: status to be incuded in the frame + * @responder: Tdls request type + * @peer_capability: peer cpabilities + * @len: length of additional Ies + * @buf: additional IEs to be included + */ +struct tdls_send_mgmt { + struct qdf_mac_addr peer_mac; + uint8_t frame_type; + uint8_t dialog; + uint16_t status_code; + uint8_t responder; + uint32_t peer_capability; + uint8_t len; + /* Variable length, do not add anything after this */ + uint8_t buf[]; +}; + +/** + * struct tdls_validate_action_req - tdls validate mgmt request + * @vdev: vdev object + * @action_code: action code + * @peer_mac: peer mac address + * @dialog_token: dialog code + * @status_code: status code to add + * @len: len of the frame + * @responder: whether to respond or not + * @max_sta_failed: mgmt failure reason + */ +struct tdls_validate_action_req { + struct wlan_objmgr_vdev *vdev; + uint8_t action_code; + uint8_t peer_mac[QDF_MAC_ADDR_SIZE]; + uint8_t dialog_token; + uint8_t status_code; + size_t len; + int responder; + int max_sta_failed; +}; + +/** + * struct tdls_get_all_peers - get all peers from the list + * @vdev: vdev object + * @buf: output string buffer to hold the peer info + * @buf_len: the size of output string buffer + */ +struct tdls_get_all_peers { + struct wlan_objmgr_vdev *vdev; + char *buf; + int buf_len; +}; + +/** + * struct tdls_send_action_frame_request - tdls send mgmt request + * @vdev: vdev object + * @chk_frame: frame validation structure + * @session_id: session id + * @vdev_id: vdev id + * @cmd_buf: cmd buffer + * @len: length of the frame + * @use_default_ac: access category + * @tdls_mgmt: tdls management + */ +struct tdls_action_frame_request { + struct wlan_objmgr_vdev *vdev; + struct tdls_validate_action_req *chk_frame; + uint8_t session_id; + uint8_t vdev_id; + const uint8_t *cmd_buf; + uint8_t len; + bool use_default_ac; + /* Variable length, do not add anything after this */ + struct tdls_send_mgmt tdls_mgmt; +}; + +/** + * struct tdls_set_responder_req - tdls set responder in peer + * @vdev: vdev object + * @peer_mac: peer mac address + * @responder: whether to respond or not + */ +struct tdls_set_responder_req { + struct wlan_objmgr_vdev *vdev; + uint8_t peer_mac[QDF_MAC_ADDR_SIZE]; + uint8_t responder; +}; + +/** + * struct tdls_sta_notify_params - STA connection notify info + * @vdev: vdev object + * @tdls_prohibited: peer mac addr + * @tdls_chan_swit_prohibited: peer type + * @lfr_roam: is trigger due to lfr + * @session_id: session id + */ +struct tdls_sta_notify_params { + struct wlan_objmgr_vdev *vdev; + bool tdls_prohibited; + bool tdls_chan_swit_prohibited; + bool lfr_roam; + bool user_disconnect; + uint8_t session_id; +}; + +/** + * struct tdls_delete_all_peers_params - TDLS set mode params + * @vdev: vdev object + */ +struct tdls_delete_all_peers_params { + struct wlan_objmgr_vdev *vdev; +}; + +/** + * struct tdls_set_mode_params - TDLS set mode params + * @vdev: vdev object + * @tdls_mode: tdls mode to set + * @update_last: inform to update last tdls mode + * @source: mode change requester + */ +struct tdls_set_mode_params { + struct wlan_objmgr_vdev *vdev; + enum tdls_feature_mode tdls_mode; + bool update_last; + enum tdls_disable_sources source; +}; + +/** + * struct tdls_del_all_tdls_peers - delete all tdls peers + * @msg_type: type of message + * @msg_len: length of message + * @bssid: bssid of peer device + */ +struct tdls_del_all_tdls_peers { + uint16_t msg_type; + uint16_t msg_len; + struct qdf_mac_addr bssid; +}; + +/** + * struct tdls_antenna_switch_request - TDLS antenna switch request + * @vdev: vdev object + * @mode: antenna mode, 1x1 or 2x2 + */ +struct tdls_antenna_switch_request { + struct wlan_objmgr_vdev *vdev; + uint32_t mode; +}; + +/** + * struct tdls_set_offchannel - TDLS set offchannel + * @vdev: vdev object + * @offchannel: Updated tdls offchannel value. + * @callback: callback to release vdev ref. + */ +struct tdls_set_offchannel { + struct wlan_objmgr_vdev *vdev; + uint16_t offchannel; + tdls_offchan_parms_callback callback; +}; + +/** + * struct tdls_set_offchan_mode - TDLS set offchannel mode + * @vdev: vdev object + * @offchan_mode: Updated tdls offchannel mode value. + * @callback: callback to release vdev ref. + */ +struct tdls_set_offchanmode { + struct wlan_objmgr_vdev *vdev; + uint8_t offchan_mode; + tdls_offchan_parms_callback callback; +}; + +/** + * struct tdls_set_offchan_offset - TDLS set offchannel mode + * @vdev: vdev object + * @offchan_offset: Offchan offset value. + * @callback: callback to release vdev ref. + */ +struct tdls_set_secoffchanneloffset { + struct wlan_objmgr_vdev *vdev; + int offchan_offset; + tdls_offchan_parms_callback callback; +}; + +#endif diff --git a/components/tdls/dispatcher/inc/wlan_tdls_tgt_api.h b/components/tdls/dispatcher/inc/wlan_tdls_tgt_api.h new file mode 100644 index 000000000000..14a19006b997 --- /dev/null +++ b/components/tdls/dispatcher/inc/wlan_tdls_tgt_api.h @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_tgt_api.h + * + * TDLS south bound interface declaration + */ + +#ifndef _WLAN_TDLS_TGT_API_H_ +#define _WLAN_TDLS_TGT_API_H_ +#include +#include "../../core/src/wlan_tdls_main.h" + +/** + * tgt_tdls_set_fw_state() - invoke lmac tdls update fw + * @psoc: soc object + * @tdls_param: update tdls state parameters + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_tdls_set_fw_state(struct wlan_objmgr_psoc *psoc, + struct tdls_info *tdls_param); + +/** + * tgt_tdls_set_peer_state() - invoke lmac tdls update peer state + * @psoc: soc object + * @peer_param: update tdls peer parameters + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_tdls_set_peer_state(struct wlan_objmgr_psoc *psoc, + struct tdls_peer_update_state *peer_param); + +/** + * tgt_tdls_set_offchan_mode() - invoke lmac tdls set off-channel mode + * @psoc: soc object + * @param: set tdls off channel parameters + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_tdls_set_offchan_mode(struct wlan_objmgr_psoc *psoc, + struct tdls_channel_switch_params *param); + +/** + * tgt_tdls_set_uapsd()- invoke lamc tdls set uapsd function + * @psoc: soc object + * @params: uapsd parameters + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_tdls_set_uapsd(struct wlan_objmgr_psoc *psoc, + struct sta_uapsd_trig_params *params); + +/** + * tgt_tdls_send_mgmt_rsp() - process tdls mgmt response + * @pmsg: sheduler msg + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_tdls_send_mgmt_rsp(struct scheduler_msg *pmsg); + +/** + * tgt_tdls_send_mgmt_tx_completion() -process tx completion message + * @pmsg: sheduler msg + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_tdls_send_mgmt_tx_completion(struct scheduler_msg *pmsg); + +/** + * tgt_tdls_del_peer_rsp() - handle TDLS del peer response + * @pmsg: sheduler msg + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_tdls_del_peer_rsp(struct scheduler_msg *pmsg); + +/** + * tgt_tdls_add_peer_rsp() - handle TDLS add peer response + * @pmsg: sheduler msg + * + * Return: QDF_STATUS + */ +QDF_STATUS tgt_tdls_add_peer_rsp(struct scheduler_msg *pmsg); + +/** + * tgt_tdls_register_ev_handler() - invoke lmac register tdls event handler + * @psoc: soc object + * + * Return: QDF_STATUS_SUCCESS for success or error code. + */ +QDF_STATUS tgt_tdls_register_ev_handler(struct wlan_objmgr_psoc *psoc); + +/** + * tgt_tdls_unregister_ev_handler() - invoke lmac unregister tdls event handler + * @psoc: soc object + * + * Return: QDF_STATUS_SUCCESS for success or error code. + */ +QDF_STATUS tgt_tdls_unregister_ev_handler(struct wlan_objmgr_psoc *psoc); + +/** + * tgt_tdls_event_handler() - The callback registered to WMI for tdls events + * @psoc: psoc object + * @info: tdls event info + * + * The callback is registered by tgt as tdls rx ops handler. + * + * Return: 0 for success or err code. + */ +QDF_STATUS +tgt_tdls_event_handler(struct wlan_objmgr_psoc *psoc, + struct tdls_event_info *info); + +/** + * tgt_tdls_mgmt_frame_rx_cb() - callback for rx mgmt frame + * @psoc: soc context + * @peer: peer context + * @buf: rx buffer + * @mgmt_rx_params: mgmt rx parameters + * @frm_type: frame type + * + * This function gets called from mgmt tx/rx component when rx mgmt + * received. + * + * Return: QDF_STATUS_SUCCESS + */ +QDF_STATUS tgt_tdls_mgmt_frame_rx_cb(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_peer *peer, qdf_nbuf_t buf, + struct mgmt_rx_event_params *mgmt_rx_params, + enum mgmt_frame_type frm_type); + +/** + * tgt_tdls_peers_deleted_notification()- notification from legacy lim + * @psoc: soc object + * @session_id: session id + * + * This function called from legacy lim to notify tdls peer deletion + * + * Return: None + */ +void tgt_tdls_peers_deleted_notification(struct wlan_objmgr_psoc *psoc, + uint32_t session_id); + +/** + * tgt_tdls_delete_all_peers_indication()- Indication to tdls component + * @psoc: soc object + * @session_id: session id + * + * This function called from legacy lim to tdls component to delete tdls peers. + * + * Return: None + */ +void tgt_tdls_delete_all_peers_indication(struct wlan_objmgr_psoc *psoc, + uint32_t session_id); + +#endif diff --git a/components/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h b/components/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h new file mode 100644 index 000000000000..3d6570772c64 --- /dev/null +++ b/components/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_ucfg_api.h + * + * TDLS north bound interface declaration + */ + +#if !defined(_WLAN_TDLS_UCFG_API_H_) +#define _WLAN_TDLS_UCFG_API_H_ + +#include +#include +#include +#include +#include +#include + +/** + * ucfg_tdls_init() - TDLS module initialization API + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_init(void); + +/** + * ucfg_tdls_deinit() - TDLS module deinitialization API + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_deinit(void); + +/** + * ucfg_tdls_psoc_open() - TDLS module psoc open API + * @psoc: psoc object + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_psoc_open(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_tdls_psoc_close() - TDLS module psoc close API + * @psoc: psoc object + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_psoc_close(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_tdls_psoc_start() - TDLS module start + * @psoc: psoc object + * @req: tdls start paramets + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_update_config(struct wlan_objmgr_psoc *psoc, + struct tdls_start_params *req); + +/** + * ucfg_tdls_psoc_enable() - TDLS module enable API + * @psoc: psoc object + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_psoc_enable(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_tdls_psoc_disable() - TDLS moudle disable API + * @psoc: psoc object + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_psoc_disable(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); + +/** + * ucfg_tdls_oper() - handle TDLS oper functions + * @vdev: vdev object + * @macaddr: MAC address of TDLS peer + * @cmd: oper cmd + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_oper(struct wlan_objmgr_vdev *vdev, + const uint8_t *macaddr, enum tdls_command_type cmd); + +/** + * ucfg_tdls_get_all_peers() - get all tdls peers from the list + * @vdev: vdev object + * @buf: output buffer + * @buflen: length of written data + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_get_all_peers(struct wlan_objmgr_vdev *vdev, + char *buf, int buflen); + +/** + * ucfg_tdls_send_mgmt_frame() - send TDLS mgmt frame + * @mgmt_req: pointer to TDLS action frame request struct + * + * This will TDLS action frames to peer + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_send_mgmt_frame( + struct tdls_action_frame_request *mgmt_req); + +/** + * ucfg_tdls_responder() - set responder in TDLS peer + * @msg_req: responder msg + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_responder(struct tdls_set_responder_req *msg_req); + +/** + * ucfg_tdls_teardown_links() - teardown all TDLS links + * @vdev: vdev object manager + * + * Return: None + */ +QDF_STATUS ucfg_tdls_teardown_links(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_tdls_notify_reset_adapter() - notify reset adapter + * @vdev: vdev object manager + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_notify_reset_adapter(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_tdls_notify_sta_connect() - notify sta connect + * @notify_info: sta notification info + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_notify_sta_connect( + struct tdls_sta_notify_params *notify_info); + +/** + * ucfg_tdls_notify_sta_disconnect() - notify sta disconnect + * @notify_info: sta notification info + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_notify_sta_disconnect( + struct tdls_sta_notify_params *notify_info); + +/** + * ucfg_tdls_set_operating_mode() - set operating mode + * @set_mode_params: set mode params + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_set_operating_mode( + struct tdls_set_mode_params *set_mode_params); + +/** + * ucfg_tdls_update_rx_pkt_cnt() - update rx pkt count + * @vdev: tdls vdev object + * @mac_addr: peer mac address + * + * Return: None + */ +void ucfg_tdls_update_rx_pkt_cnt(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *mac_addr); + +/** + * ucfg_tdls_update_tx_pkt_cnt() - update tx pkt count + * @vdev: tdls vdev object + * @mac_addr: peer mac address + * + * Return: None + */ +void ucfg_tdls_update_tx_pkt_cnt(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *mac_addr); + +/** + * ucfg_tdls_antenna_switch() - tdls antenna switch + * @vdev: tdls vdev object + * @mode: antenna mode + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_tdls_antenna_switch(struct wlan_objmgr_vdev *vdev, + uint32_t mode); + +/** + * ucfg_set_tdls_offchannel() - Handle TDLS set offchannel + * @vdev: vdev object + * @offchannel: updated offchannel + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_set_tdls_offchannel(struct wlan_objmgr_vdev *vdev, + int offchannel); + +/** + * ucfg_set_tdls_offchan_mode() - Handle TDLS set offchannel mode + * @vdev: vdev object + * @offchanmode: updated off-channel mode + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_set_tdls_offchan_mode(struct wlan_objmgr_vdev *vdev, + int offchanmode); + +/** + * ucfg_set_tdls_secoffchanneloffset() - Handle TDLS set offchannel offset + * @vdev: vdev object + * @offchanoffset: tdls off-channel offset + * + * Return: QDF_STATUS + */ +QDF_STATUS ucfg_set_tdls_secoffchanneloffset(struct wlan_objmgr_vdev *vdev, + int offchanoffset); +#endif diff --git a/components/tdls/dispatcher/src/wlan_tdls_cfg.c b/components/tdls/dispatcher/src/wlan_tdls_cfg.c new file mode 100644 index 000000000000..b9ff89221213 --- /dev/null +++ b/components/tdls/dispatcher/src/wlan_tdls_cfg.c @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: This file contains TDLS configures interface definitions + */ + +#include +#include "wlan_tdls_cfg_api.h" +#include "../../core/src/wlan_tdls_main.h" + +QDF_STATUS +cfg_tdls_get_support_enable(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = false; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_support_enable; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_set_support_enable(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + soc_obj->tdls_configs.tdls_support_enable = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_get_external_control(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = false; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_external_control; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_get_uapsd_mask(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = 0; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_uapsd_mask; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_get_buffer_sta_enable(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = false; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_buffer_sta_enable; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_set_buffer_sta_enable(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + soc_obj->tdls_configs.tdls_buffer_sta_enable = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_get_uapsd_inactivity_time(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = 0; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_uapsd_inactivity_time; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_get_rx_pkt_threshold(struct wlan_objmgr_psoc *psoc, + uint32_t *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = 0; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_rx_pkt_threshold; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_get_off_channel_enable(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = false; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_off_chan_enable; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_set_off_channel_enable(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + soc_obj->tdls_configs.tdls_off_chan_enable = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_get_wmm_mode_enable(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = false; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_wmm_mode_enable; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_set_vdev_nss_2g(struct wlan_objmgr_psoc *psoc, + uint8_t val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + soc_obj->tdls_configs.tdls_vdev_nss_2g = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_set_vdev_nss_5g(struct wlan_objmgr_psoc *psoc, + uint8_t val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + soc_obj->tdls_configs.tdls_vdev_nss_5g = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_get_sleep_sta_enable(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = false; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_sleep_sta_enable; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_set_sleep_sta_enable(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + soc_obj->tdls_configs.tdls_sleep_sta_enable = val; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_get_scan_enable(struct wlan_objmgr_psoc *psoc, + bool *val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + *val = false; + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + *val = soc_obj->tdls_configs.tdls_scan_enable; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_tdls_set_scan_enable(struct wlan_objmgr_psoc *psoc, + bool val) +{ + struct tdls_soc_priv_obj *soc_obj; + + soc_obj = wlan_psoc_get_tdls_soc_obj(psoc); + if (!soc_obj) { + tdls_err("tdls soc null"); + return QDF_STATUS_E_INVAL; + } + + soc_obj->tdls_configs.tdls_scan_enable = val; + + return QDF_STATUS_SUCCESS; +} diff --git a/components/tdls/dispatcher/src/wlan_tdls_tgt_api.c b/components/tdls/dispatcher/src/wlan_tdls_tgt_api.c new file mode 100644 index 000000000000..bb09a0e8b4a1 --- /dev/null +++ b/components/tdls/dispatcher/src/wlan_tdls_tgt_api.c @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_tgt_api.c + * + * TDLS south bound interface definitions + */ + +#include "qdf_status.h" +#include +#include "../../core/src/wlan_tdls_main.h" +#include "../../core/src/wlan_tdls_cmds_process.h" +#include "../../core/src/wlan_tdls_mgmt.h" + +static inline struct wlan_lmac_if_tdls_tx_ops * +wlan_psoc_get_tdls_txops(struct wlan_objmgr_psoc *psoc) +{ + return &psoc->soc_cb.tx_ops.tdls_tx_ops; +} + +static inline struct wlan_lmac_if_tdls_rx_ops * +wlan_psoc_get_tdls_rxops(struct wlan_objmgr_psoc *psoc) +{ + return &psoc->soc_cb.rx_ops.tdls_rx_ops; +} + +QDF_STATUS tgt_tdls_set_fw_state(struct wlan_objmgr_psoc *psoc, + struct tdls_info *tdls_param) +{ + struct wlan_lmac_if_tdls_tx_ops *tdls_ops = NULL; + + tdls_ops = wlan_psoc_get_tdls_txops(psoc); + if (tdls_ops && tdls_ops->update_fw_state) + return tdls_ops->update_fw_state(psoc, tdls_param); + else + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tgt_tdls_set_peer_state(struct wlan_objmgr_psoc *psoc, + struct tdls_peer_update_state *peer_param) +{ + struct wlan_lmac_if_tdls_tx_ops *tdls_ops = NULL; + + tdls_ops = wlan_psoc_get_tdls_txops(psoc); + if (tdls_ops && tdls_ops->update_peer_state) + return tdls_ops->update_peer_state(psoc, peer_param); + else + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tgt_tdls_set_offchan_mode(struct wlan_objmgr_psoc *psoc, + struct tdls_channel_switch_params *param) +{ + struct wlan_lmac_if_tdls_tx_ops *tdls_ops = NULL; + + tdls_ops = wlan_psoc_get_tdls_txops(psoc); + if (tdls_ops && tdls_ops->set_offchan_mode) + return tdls_ops->set_offchan_mode(psoc, param); + else + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tgt_tdls_set_uapsd(struct wlan_objmgr_psoc *psoc, + struct sta_uapsd_trig_params *params) +{ + struct wlan_lmac_if_tdls_tx_ops *tdls_ops = NULL; + + tdls_ops = wlan_psoc_get_tdls_txops(psoc); + if (tdls_ops && tdls_ops->tdls_set_uapsd) + return tdls_ops->tdls_set_uapsd(psoc, params); + else + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tgt_tdls_send_mgmt_tx_completion(struct scheduler_msg *pmsg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!pmsg || !pmsg->bodyptr) { + tdls_err("msg: 0x%pK", pmsg); + QDF_ASSERT(0); + return QDF_STATUS_E_NULL_VALUE; + } + + status = tdls_send_mgmt_tx_completion(pmsg->bodyptr); + + return status; +} + +QDF_STATUS tgt_tdls_send_mgmt_rsp(struct scheduler_msg *pmsg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!pmsg || !pmsg->bodyptr) { + tdls_err("msg: 0x%pK", pmsg); + QDF_ASSERT(0); + return QDF_STATUS_E_NULL_VALUE; + } + + status = tdls_process_send_mgmt_rsp(pmsg->bodyptr); + + return status; +} + +QDF_STATUS tgt_tdls_add_peer_rsp(struct scheduler_msg *pmsg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!pmsg || !pmsg->bodyptr) { + tdls_err("msg: 0x%pK", pmsg); + QDF_ASSERT(0); + return QDF_STATUS_E_NULL_VALUE; + } + + status = tdls_process_add_peer_rsp(pmsg->bodyptr); + + return status; +} + +QDF_STATUS tgt_tdls_del_peer_rsp(struct scheduler_msg *pmsg) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + + if (!pmsg || !pmsg->bodyptr) { + tdls_err("msg: 0x%pK", pmsg); + QDF_ASSERT(0); + return QDF_STATUS_E_NULL_VALUE; + } + + status = tdls_process_del_peer_rsp(pmsg->bodyptr); + + return status; +} + +QDF_STATUS tgt_tdls_register_ev_handler(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_lmac_if_tdls_tx_ops *tdls_ops = NULL; + + tdls_ops = wlan_psoc_get_tdls_txops(psoc); + if (tdls_ops && tdls_ops->tdls_reg_ev_handler) + return tdls_ops->tdls_reg_ev_handler(psoc, NULL); + else + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tgt_tdls_unregister_ev_handler(struct wlan_objmgr_psoc *psoc) +{ + struct wlan_lmac_if_tdls_tx_ops *tdls_ops = NULL; + + tdls_ops = wlan_psoc_get_tdls_txops(psoc); + if (tdls_ops->tdls_unreg_ev_handler) + return tdls_ops->tdls_unreg_ev_handler(psoc, NULL); + else + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS tgt_tdls_event_flush_cb(struct scheduler_msg *msg) +{ + struct tdls_event_notify *notify; + + notify = msg->bodyptr; + if (notify && notify->vdev) { + wlan_objmgr_vdev_release_ref(notify->vdev, WLAN_TDLS_SB_ID); + qdf_mem_free(notify); + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +tgt_tdls_event_handler(struct wlan_objmgr_psoc *psoc, + struct tdls_event_info *info) +{ + struct scheduler_msg msg = {0,}; + struct tdls_event_notify *notify; + uint8_t vdev_id; + QDF_STATUS status; + + if (!psoc || !info) { + tdls_err("psoc: 0x%pK, info: 0x%pK", psoc, info); + return QDF_STATUS_E_NULL_VALUE; + } + tdls_debug("vdev: %d, type: %d, reason: %d" QDF_MAC_ADDR_STR, + info->vdev_id, info->message_type, info->peer_reason, + QDF_MAC_ADDR_ARRAY(info->peermac.bytes)); + notify = qdf_mem_malloc(sizeof(*notify)); + if (!notify) { + tdls_err("mem allocate fail"); + return QDF_STATUS_E_NOMEM; + } + + vdev_id = info->vdev_id; + notify->vdev = + wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + vdev_id, WLAN_TDLS_SB_ID); + if (!notify->vdev) { + tdls_err("null vdev, vdev_id: %d, psoc: 0x%pK", vdev_id, psoc); + return QDF_STATUS_E_INVAL; + } + qdf_mem_copy(¬ify->event, info, sizeof(*info)); + + msg.bodyptr = notify; + msg.callback = tdls_process_evt; + msg.flush_callback = tgt_tdls_event_flush_cb; + + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't post msg to handle tdls event"); + wlan_objmgr_vdev_release_ref(notify->vdev, WLAN_TDLS_SB_ID); + qdf_mem_free(notify); + } + + return status; +} + +static QDF_STATUS tgt_tdls_mgmt_frame_rx_flush_cb(struct scheduler_msg *msg) +{ + struct tdls_rx_mgmt_event *rx_mgmt_event; + + rx_mgmt_event = msg->bodyptr; + + if (rx_mgmt_event) { + if (rx_mgmt_event->rx_mgmt) + qdf_mem_free(rx_mgmt_event->rx_mgmt); + + qdf_mem_free(rx_mgmt_event); + } + msg->bodyptr = NULL; + + return QDF_STATUS_SUCCESS; +} + +static +QDF_STATUS tgt_tdls_mgmt_frame_process_rx_cb( + struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_peer *peer, + qdf_nbuf_t buf, + struct mgmt_rx_event_params *mgmt_rx_params, + enum mgmt_frame_type frm_type) +{ + struct tdls_rx_mgmt_frame *rx_mgmt; + struct tdls_rx_mgmt_event *rx_mgmt_event; + struct tdls_soc_priv_obj *tdls_soc_obj; + struct scheduler_msg msg = {0}; + struct wlan_objmgr_vdev *vdev; + uint32_t vdev_id; + uint8_t *pdata; + QDF_STATUS status; + + tdls_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_TDLS); + if (!tdls_soc_obj) { + tdls_err("tdls ctx is NULL, drop this frame"); + return QDF_STATUS_E_FAILURE; + } + + if (!peer) { + vdev = tdls_get_vdev(psoc, WLAN_TDLS_SB_ID); + if (!vdev) { + tdls_err("current tdls vdev is null, can't get vdev id"); + return QDF_STATUS_E_FAILURE; + } + vdev_id = wlan_vdev_get_id(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID); + } else { + vdev = wlan_peer_get_vdev(peer); + if (!vdev) { + tdls_err("vdev is NULL in peer, drop this frame"); + return QDF_STATUS_E_FAILURE; + } + vdev_id = wlan_vdev_get_id(vdev); + } + + rx_mgmt_event = qdf_mem_malloc(sizeof(*rx_mgmt_event)); + if (!rx_mgmt_event) { + tdls_err("Failed to allocate rx mgmt event"); + return QDF_STATUS_E_NOMEM; + } + + rx_mgmt = qdf_mem_malloc(sizeof(*rx_mgmt) + + mgmt_rx_params->buf_len); + if (!rx_mgmt) { + tdls_err("Failed to allocate rx mgmt frame"); + qdf_mem_free(rx_mgmt_event); + return QDF_STATUS_E_NOMEM; + } + + pdata = (uint8_t *)qdf_nbuf_data(buf); + rx_mgmt->frame_len = mgmt_rx_params->buf_len; + rx_mgmt->rx_chan = mgmt_rx_params->channel; + rx_mgmt->vdev_id = vdev_id; + rx_mgmt->frm_type = frm_type; + rx_mgmt->rx_rssi = mgmt_rx_params->rssi; + + rx_mgmt_event->rx_mgmt = rx_mgmt; + rx_mgmt_event->tdls_soc_obj = tdls_soc_obj; + qdf_mem_copy(rx_mgmt->buf, pdata, mgmt_rx_params->buf_len); + msg.type = TDLS_EVENT_RX_MGMT; + msg.bodyptr = rx_mgmt_event; + msg.callback = tdls_process_rx_frame; + msg.flush_callback = tgt_tdls_mgmt_frame_rx_flush_cb; + status = scheduler_post_message(QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(rx_mgmt); + qdf_mem_free(rx_mgmt_event); + } + + qdf_nbuf_free(buf); + + return status; +} + +QDF_STATUS tgt_tdls_mgmt_frame_rx_cb( + struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_peer *peer, + qdf_nbuf_t buf, + struct mgmt_rx_event_params *mgmt_rx_params, + enum mgmt_frame_type frm_type) +{ + QDF_STATUS status; + + tdls_debug("psoc:%pK, peer:%pK, type:%d", psoc, peer, frm_type); + + + if (!buf) { + tdls_err("rx frame buff is null buf:%pK", buf); + return QDF_STATUS_E_INVAL; + } + + if (!mgmt_rx_params || !psoc) { + tdls_err("input is NULL mgmt_rx_params:%pK psoc:%pK, peer:%pK", + mgmt_rx_params, psoc, peer); + status = QDF_STATUS_E_INVAL; + goto release_nbuf; + } + + status = wlan_objmgr_peer_try_get_ref(peer, WLAN_TDLS_SB_ID); + if (QDF_STATUS_SUCCESS != status) + goto release_nbuf; + + status = tgt_tdls_mgmt_frame_process_rx_cb(psoc, peer, buf, + mgmt_rx_params, frm_type); + + wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_SB_ID); + + if (QDF_STATUS_SUCCESS != status) +release_nbuf: + qdf_nbuf_free(buf); + return status; +} + +void tgt_tdls_peers_deleted_notification(struct wlan_objmgr_psoc *psoc, + uint32_t session_id) +{ + tdls_peers_deleted_notification(psoc, session_id); +} + +void tgt_tdls_delete_all_peers_indication(struct wlan_objmgr_psoc *psoc, + uint32_t session_id) +{ + + tdls_delete_all_peers_indication(psoc, session_id); +} diff --git a/components/tdls/dispatcher/src/wlan_tdls_ucfg_api.c b/components/tdls/dispatcher/src/wlan_tdls_ucfg_api.c new file mode 100644 index 000000000000..cd86729d8a02 --- /dev/null +++ b/components/tdls/dispatcher/src/wlan_tdls_ucfg_api.c @@ -0,0 +1,1127 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: wlan_tdls_ucfg_api.c + * + * TDLS north bound interface definitions + */ + +#include +#include +#include "../../core/src/wlan_tdls_main.h" +#include "../../core/src/wlan_tdls_cmds_process.h" +#include "../../core/src/wlan_tdls_ct.h" +#include "../../core/src/wlan_tdls_mgmt.h" +#include +#include +#include "wlan_policy_mgr_api.h" +#include "wlan_scan_ucfg_api.h" +#include "wlan_tdls_cfg.h" +#include "cfg_ucfg_api.h" + +QDF_STATUS ucfg_tdls_init(void) +{ + QDF_STATUS status; + + tdls_notice("tdls module dispatcher init"); + status = wlan_objmgr_register_psoc_create_handler(WLAN_UMAC_COMP_TDLS, + tdls_psoc_obj_create_notification, NULL); + + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("Failed to register psoc create handler for tdls"); + return status; + } + + status = wlan_objmgr_register_psoc_destroy_handler(WLAN_UMAC_COMP_TDLS, + tdls_psoc_obj_destroy_notification, NULL); + + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("Failed to register psoc delete handler for tdls"); + goto fail_delete_psoc; + } + + status = wlan_objmgr_register_vdev_create_handler(WLAN_UMAC_COMP_TDLS, + tdls_vdev_obj_create_notification, NULL); + + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("Failed to register vdev create handler for tdls"); + goto fail_create_vdev; + } + + status = wlan_objmgr_register_vdev_destroy_handler(WLAN_UMAC_COMP_TDLS, + tdls_vdev_obj_destroy_notification, NULL); + + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("Failed to register vdev create handler for tdls"); + goto fail_delete_vdev; + } + tdls_notice("tdls module dispatcher init done"); + + return status; +fail_delete_vdev: + wlan_objmgr_unregister_vdev_create_handler(WLAN_UMAC_COMP_TDLS, + tdls_vdev_obj_create_notification, NULL); + +fail_create_vdev: + wlan_objmgr_unregister_psoc_destroy_handler(WLAN_UMAC_COMP_TDLS, + tdls_psoc_obj_destroy_notification, NULL); + +fail_delete_psoc: + wlan_objmgr_unregister_psoc_create_handler(WLAN_UMAC_COMP_TDLS, + tdls_psoc_obj_create_notification, NULL); + + return status; +} + +QDF_STATUS ucfg_tdls_deinit(void) +{ + QDF_STATUS ret; + + tdls_notice("tdls module dispatcher deinit"); + ret = wlan_objmgr_unregister_psoc_create_handler(WLAN_UMAC_COMP_TDLS, + tdls_psoc_obj_create_notification, NULL); + if (QDF_IS_STATUS_ERROR(ret)) + tdls_err("Failed to unregister psoc create handler"); + + ret = wlan_objmgr_unregister_psoc_destroy_handler(WLAN_UMAC_COMP_TDLS, + tdls_psoc_obj_destroy_notification, NULL); + if (QDF_IS_STATUS_ERROR(ret)) + tdls_err("Failed to unregister psoc delete handler"); + + ret = wlan_objmgr_unregister_vdev_create_handler(WLAN_UMAC_COMP_TDLS, + tdls_vdev_obj_create_notification, NULL); + if (QDF_IS_STATUS_ERROR(ret)) + tdls_err("Failed to unregister vdev create handler"); + + ret = wlan_objmgr_unregister_vdev_destroy_handler(WLAN_UMAC_COMP_TDLS, + tdls_vdev_obj_destroy_notification, NULL); + + if (QDF_IS_STATUS_ERROR(ret)) + tdls_err("Failed to unregister vdev delete handler"); + + return ret; +} + +/** + * tdls_update_feature_flag() - update tdls feature flag + * @tdls_soc_obj: pointer to tdls psoc object + * + * This function updates tdls feature flag + */ +static void +tdls_update_feature_flag(struct tdls_soc_priv_obj *tdls_soc_obj) +{ + tdls_soc_obj->tdls_configs.tdls_feature_flags = + ((tdls_soc_obj->tdls_configs.tdls_off_chan_enable ? + 1 << TDLS_FEATURE_OFF_CHANNEL : 0) | + (tdls_soc_obj->tdls_configs.tdls_wmm_mode_enable ? + 1 << TDLS_FEATURE_WMM : 0) | + (tdls_soc_obj->tdls_configs.tdls_buffer_sta_enable ? + 1 << TDLS_FEATURE_BUFFER_STA : 0) | + (tdls_soc_obj->tdls_configs.tdls_sleep_sta_enable ? + 1 << TDLS_FEATURE_SLEEP_STA : 0) | + (tdls_soc_obj->tdls_configs.tdls_scan_enable ? + 1 << TDLS_FEATURE_SCAN : 0) | + (tdls_soc_obj->tdls_configs.tdls_support_enable ? + 1 << TDLS_FEATURE_ENABLE : 0) | + (tdls_soc_obj->tdls_configs.tdls_implicit_trigger_enable ? + 1 << TDLS_FEAUTRE_IMPLICIT_TRIGGER : 0) | + (tdls_soc_obj->tdls_configs.tdls_external_control ? + 1 << TDLS_FEATURE_EXTERNAL_CONTROL : 0)); +} + +/** + * tdls_object_init_params() - init parameters for tdls object + * @tdls_soc_obj: pointer to tdls psoc object + * + * This function init parameters for tdls object + */ +static QDF_STATUS tdls_object_init_params( + struct tdls_soc_priv_obj *tdls_soc_obj) +{ + struct wlan_objmgr_psoc *psoc; + + if (!tdls_soc_obj) { + tdls_err("invalid param"); + return QDF_STATUS_E_INVAL; + } + + psoc = tdls_soc_obj->soc; + if (!psoc) { + tdls_err("invalid psoc object"); + return QDF_STATUS_E_INVAL; + } + + tdls_soc_obj->tdls_configs.tdls_tx_states_period = + cfg_get(psoc, CFG_TDLS_TX_STATS_PERIOD); + tdls_soc_obj->tdls_configs.tdls_tx_pkt_threshold = + cfg_get(psoc, CFG_TDLS_TX_PACKET_THRESHOLD); + tdls_soc_obj->tdls_configs.tdls_rx_pkt_threshold = + cfg_get(psoc, CFG_TDLS_PUAPSD_RX_FRAME_THRESHOLD); + tdls_soc_obj->tdls_configs.tdls_max_discovery_attempt = + cfg_get(psoc, CFG_TDLS_MAX_DISCOVERY_ATTEMPT); + tdls_soc_obj->tdls_configs.tdls_idle_timeout = + cfg_get(psoc, CFG_TDLS_IDLE_TIMEOUT); + tdls_soc_obj->tdls_configs.tdls_idle_pkt_threshold = + cfg_get(psoc, CFG_TDLS_IDLE_PACKET_THRESHOLD); + tdls_soc_obj->tdls_configs.tdls_rssi_trigger_threshold = + cfg_get(psoc, CFG_TDLS_RSSI_TRIGGER_THRESHOLD); + tdls_soc_obj->tdls_configs.tdls_rssi_teardown_threshold = + cfg_get(psoc, CFG_TDLS_RSSI_TEARDOWN_THRESHOLD); + tdls_soc_obj->tdls_configs.tdls_rssi_delta = + cfg_get(psoc, CFG_TDLS_RSSI_DELTA); + tdls_soc_obj->tdls_configs.tdls_uapsd_mask = + cfg_get(psoc, CFG_TDLS_QOS_WMM_UAPSD_MASK); + tdls_soc_obj->tdls_configs.tdls_uapsd_inactivity_time = + cfg_get(psoc, CFG_TDLS_PUAPSD_INACT_TIME); + tdls_soc_obj->tdls_configs.tdls_uapsd_pti_window = + cfg_get(psoc, CFG_TDLS_PUAPSD_PEER_TRAFFIC_IND_WINDOW); + tdls_soc_obj->tdls_configs.tdls_uapsd_ptr_timeout = + cfg_get(psoc, CFG_TDLS_PUAPSD_PEER_TRAFFIC_RSP_TIMEOUT); + tdls_soc_obj->tdls_configs.tdls_pre_off_chan_num = + cfg_get(psoc, CFG_TDLS_PREFERRED_OFF_CHANNEL_NUM); + tdls_soc_obj->tdls_configs.tdls_pre_off_chan_bw = + cfg_get(psoc, CFG_TDLS_PREFERRED_OFF_CHANNEL_BW); + tdls_soc_obj->tdls_configs.tdls_peer_kickout_threshold = + cfg_get(psoc, CFG_TDLS_PEER_KICKOUT_THRESHOLD); + tdls_soc_obj->tdls_configs.delayed_trig_framint = + cfg_get(psoc, CFG_TDLS_DELAYED_TRGR_FRM_INT); + tdls_soc_obj->tdls_configs.tdls_wmm_mode_enable = + cfg_get(psoc, CFG_TDLS_WMM_MODE_ENABLE); + tdls_soc_obj->tdls_configs.tdls_off_chan_enable = + cfg_get(psoc, CFG_TDLS_OFF_CHANNEL_ENABLED); + tdls_soc_obj->tdls_configs.tdls_buffer_sta_enable = + cfg_get(psoc, CFG_TDLS_BUF_STA_ENABLED); + tdls_soc_obj->tdls_configs.tdls_scan_enable = + cfg_get(psoc, CFG_TDLS_SCAN_ENABLE); + tdls_soc_obj->tdls_configs.tdls_support_enable = + cfg_get(psoc, CFG_TDLS_SUPPORT_ENABLE); + tdls_soc_obj->tdls_configs.tdls_implicit_trigger_enable = + cfg_get(psoc, CFG_TDLS_IMPLICIT_TRIGGER); + tdls_soc_obj->tdls_configs.tdls_external_control = + cfg_get(psoc, CFG_TDLS_EXTERNAL_CONTROL); + + tdls_update_feature_flag(tdls_soc_obj); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS tdls_global_init(struct tdls_soc_priv_obj *soc_obj) +{ + uint8_t sta_idx; + uint32_t feature; + + tdls_object_init_params(soc_obj); + soc_obj->connected_peer_count = 0; + soc_obj->tdls_nss_switch_in_progress = false; + soc_obj->tdls_teardown_peers_cnt = 0; + soc_obj->tdls_nss_teardown_complete = false; + soc_obj->tdls_nss_transition_mode = TDLS_NSS_TRANSITION_S_UNKNOWN; + + 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 = INVALID_TDLS_PEER_ID; + soc_obj->tdls_conn_info[sta_idx].session_id = 255; + qdf_mem_zero(&soc_obj->tdls_conn_info[sta_idx].peer_mac, + QDF_MAC_ADDR_SIZE); + } + soc_obj->enable_tdls_connection_tracker = false; + soc_obj->tdls_external_peer_count = 0; + soc_obj->tdls_disable_in_progress = false; + + qdf_spinlock_create(&soc_obj->tdls_ct_spinlock); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS tdls_global_deinit(struct tdls_soc_priv_obj *soc_obj) +{ + qdf_spinlock_destroy(&soc_obj->tdls_ct_spinlock); + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_tdls_psoc_open(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct tdls_soc_priv_obj *soc_obj; + + tdls_debug("tdls psoc open"); + soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_TDLS); + if (!soc_obj) { + tdls_err("Failed to get tdls psoc component"); + return QDF_STATUS_E_FAILURE; + } + + status = tdls_global_init(soc_obj); + + return status; +} + +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; + struct policy_mgr_tdls_cbacks tdls_pm_call_backs; + + tdls_debug("tdls update config "); + if (!psoc || !req) { + tdls_err("psoc: 0x%pK, req: 0x%pK", psoc, req); + return QDF_STATUS_E_FAILURE; + } + + soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_TDLS); + if (!soc_obj) { + tdls_err("Failed to get tdls psoc component"); + return QDF_STATUS_E_FAILURE; + } + + soc_obj->tdls_rx_cb = req->tdls_rx_cb; + soc_obj->tdls_rx_cb_data = req->tdls_rx_cb_data; + + soc_obj->tdls_wmm_cb = req->tdls_wmm_cb; + soc_obj->tdls_wmm_cb_data = req->tdls_wmm_cb_data; + + soc_obj->tdls_event_cb = req->tdls_event_cb; + soc_obj->tdls_evt_cb_data = req->tdls_evt_cb_data; + + /* Save callbacks to register/deregister TDLS sta with datapath */ + soc_obj->tdls_reg_peer = req->tdls_reg_peer; + soc_obj->tdls_dereg_peer = req->tdls_dereg_peer; + soc_obj->tdls_peer_context = req->tdls_peer_context; + + /* 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; + soc_obj->tdls_del_all_peers = req->tdls_del_all_peers; + soc_obj->tdls_update_dp_vdev_flags = req->tdls_update_dp_vdev_flags; + soc_obj->tdls_dp_vdev_update = req->tdls_dp_vdev_update; + tdls_pm_call_backs.tdls_notify_increment_session = + tdls_notify_increment_session; + + tdls_pm_call_backs.tdls_notify_decrement_session = + tdls_notify_decrement_session; + if (QDF_STATUS_SUCCESS != policy_mgr_register_tdls_cb( + psoc, &tdls_pm_call_backs)) { + tdls_err("policy manager callback registration failed "); + return QDF_STATUS_E_FAILURE; + } + + tdls_update_feature_flag(soc_obj); + 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; + + soc_obj->tdls_last_mode = soc_obj->tdls_current_mode; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_tdls_psoc_enable(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + + tdls_notice("psoc tdls enable: 0x%pK", psoc); + if (!psoc) { + tdls_err("NULL psoc"); + return QDF_STATUS_E_FAILURE; + } + + status = tgt_tdls_register_ev_handler(psoc); + + if (status != QDF_STATUS_SUCCESS) + return status; + + status = wlan_serialization_register_comp_info_cb(psoc, + WLAN_UMAC_COMP_TDLS, + WLAN_SER_CMD_SCAN, + tdls_scan_serialization_comp_info_cb); + if (QDF_STATUS_SUCCESS != status) { + tdls_err("Serialize scan cmd register failed "); + return status; + } + + /* register callbacks with tx/rx mgmt */ + status = tdls_mgmt_rx_ops(psoc, true); + if (status != QDF_STATUS_SUCCESS) + tdls_err("Failed to register mgmt rx callback, status:%d", + status); + return status; +} + +QDF_STATUS ucfg_tdls_psoc_disable(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status; + struct tdls_soc_priv_obj *soc_obj = NULL; + + tdls_notice("psoc tdls disable: 0x%pK", psoc); + if (!psoc) { + tdls_err("NULL psoc"); + return QDF_STATUS_E_FAILURE; + } + + status = tgt_tdls_unregister_ev_handler(psoc); + if (QDF_IS_STATUS_ERROR(status)) + tdls_err("Failed to unregister tdls event handler"); + + status = tdls_mgmt_rx_ops(psoc, false); + if (QDF_IS_STATUS_ERROR(status)) + tdls_err("Failed to unregister mgmt rx callback"); + + soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_TDLS); + if (!soc_obj) { + tdls_err("Failed to get tdls psoc component"); + return QDF_STATUS_E_FAILURE; + } + + soc_obj->tdls_event_cb = NULL; + soc_obj->tdls_evt_cb_data = NULL; + + return status; +} + +QDF_STATUS ucfg_tdls_psoc_close(struct wlan_objmgr_psoc *psoc) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct tdls_soc_priv_obj *tdls_soc; + + tdls_notice("tdls psoc close"); + tdls_soc = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_TDLS); + if (!tdls_soc) { + tdls_err("Failed to get tdls psoc component"); + return QDF_STATUS_E_FAILURE; + } + + status = tdls_global_deinit(tdls_soc); + + 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: %pK, req %pK", 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_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + 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: %pK, update_peer: %pK", 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_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + 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; +} + +static char *tdls_get_oper_str(enum tdls_command_type cmd_type) +{ + switch (cmd_type) { + case TDLS_CMD_ENABLE_LINK: + return "Enable_TDLS_LINK"; + case TDLS_CMD_DISABLE_LINK: + return "DISABLE_TDLS_LINK"; + case TDLS_CMD_REMOVE_FORCE_PEER: + return "REMOVE_FORCE_PEER"; + case TDLS_CMD_CONFIG_FORCE_PEER: + return "CONFIG_FORCE_PEER"; + default: + return "ERR:UNKNOWN OPER"; + } +} + +QDF_STATUS ucfg_tdls_oper(struct wlan_objmgr_vdev *vdev, + const uint8_t *macaddr, enum tdls_command_type cmd) +{ + struct scheduler_msg msg = {0,}; + struct tdls_oper_request *req; + QDF_STATUS status; + + if (!vdev || !macaddr) { + tdls_err("vdev: %pK, mac %pK", vdev, macaddr); + return QDF_STATUS_E_NULL_VALUE; + } + + tdls_debug("%s for peer " QDF_MAC_ADDR_STR, + tdls_get_oper_str(cmd), + QDF_MAC_ADDR_ARRAY(macaddr)); + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + tdls_err("%s: mem allocate fail", tdls_get_oper_str(cmd)); + return QDF_STATUS_E_NOMEM; + } + + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev"); + goto error; + } + + qdf_mem_copy(req->peer_addr, macaddr, QDF_MAC_ADDR_SIZE); + req->vdev = vdev; + + msg.bodyptr = req; + msg.callback = tdls_process_cmd; + msg.type = cmd; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("post msg for %s fail", tdls_get_oper_str(cmd)); + goto dec_ref; + } + + return status; +dec_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); +error: + qdf_mem_free(req); + return status; +} + +QDF_STATUS ucfg_tdls_get_all_peers(struct wlan_objmgr_vdev *vdev, + char *buf, int buflen) +{ + struct scheduler_msg msg = {0, }; + struct tdls_get_all_peers *tdls_peers; + QDF_STATUS status; + + tdls_peers = qdf_mem_malloc(sizeof(*tdls_peers)); + + if (!tdls_peers) { + tdls_err("mem allocate fail"); + return QDF_STATUS_E_NOMEM; + } + + tdls_peers->vdev = vdev; + tdls_peers->buf_len = buflen; + tdls_peers->buf = buf; + + msg.bodyptr = tdls_peers; + msg.callback = tdls_process_cmd; + msg.type = TDLS_CMD_GET_ALL_PEERS; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + + if (status != QDF_STATUS_SUCCESS) + qdf_mem_free(tdls_peers); + + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS tdls_send_mgmt_frame_flush_callback(struct scheduler_msg *msg) +{ + struct tdls_action_frame_request *req; + + if (!msg || !msg->bodyptr) { + tdls_err("msg or msg->bodyptr is NULL"); + return QDF_STATUS_E_NULL_VALUE; + } + req = msg->bodyptr; + if (req->vdev) + wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); + + qdf_mem_free(req); + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS ucfg_tdls_post_msg_flush_cb(struct scheduler_msg *msg) +{ + void *ptr = msg->bodyptr; + struct wlan_objmgr_vdev *vdev = NULL; + + switch (msg->type) { + case TDLS_CMD_TEARDOWN_LINKS: + case TDLS_NOTIFY_RESET_ADAPTERS: + ptr = NULL; + break; + case TDLS_NOTIFY_STA_CONNECTION: + vdev = ((struct tdls_sta_notify_params *)ptr)->vdev; + break; + case TDLS_NOTIFY_STA_DISCONNECTION: + vdev = ((struct tdls_sta_notify_params *)ptr)->vdev; + break; + case TDLS_CMD_SET_TDLS_MODE: + vdev = ((struct tdls_set_mode_params *)ptr)->vdev; + break; + case TDLS_CMD_TX_ACTION: + case TDLS_CMD_SET_RESPONDER: + break; + } + + if (vdev) + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + + if (ptr) + qdf_mem_free(ptr); + + msg->bodyptr = NULL; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_tdls_send_mgmt_frame( + struct tdls_action_frame_request *req) +{ + struct scheduler_msg msg = {0, }; + struct tdls_action_frame_request *mgmt_req; + QDF_STATUS status; + + if (!req || !req->vdev) { + tdls_err("Invalid mgmt req params %pK", req); + return QDF_STATUS_E_NULL_VALUE; + } + + mgmt_req = qdf_mem_malloc(sizeof(*mgmt_req) + + req->len); + if (!mgmt_req) { + tdls_err("mem allocate fail"); + return QDF_STATUS_E_NOMEM; + } + + qdf_mem_copy(mgmt_req, req, sizeof(*req)); + + /*populate the additional IE's */ + if ((0 != req->len) && (NULL != req->cmd_buf)) { + qdf_mem_copy(mgmt_req->tdls_mgmt.buf, req->cmd_buf, + req->len); + mgmt_req->tdls_mgmt.len = req->len; + } else { + mgmt_req->tdls_mgmt.len = 0; + } + + tdls_debug("vdev id: %d, session id : %d", mgmt_req->vdev_id, + mgmt_req->session_id); + status = wlan_objmgr_vdev_try_get_ref(req->vdev, WLAN_TDLS_NB_ID); + + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("Unable to get vdev reference for tdls module"); + goto mem_free; + } + + msg.bodyptr = mgmt_req; + msg.callback = tdls_process_cmd; + msg.flush_callback = tdls_send_mgmt_frame_flush_callback; + msg.type = TDLS_CMD_TX_ACTION; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("Failed to post the mgmt tx cmd to scheduler thread"); + goto release_ref; + } + + return status; + +release_ref: + wlan_objmgr_vdev_release_ref(req->vdev, WLAN_TDLS_NB_ID); +mem_free: + qdf_mem_free(mgmt_req); + return status; +} + +QDF_STATUS ucfg_tdls_responder(struct tdls_set_responder_req *req) +{ + struct scheduler_msg msg = {0, }; + struct tdls_set_responder_req *msg_req; + QDF_STATUS status; + + if (!req || !req->vdev) { + tdls_err("invalid input %pK", req); + return QDF_STATUS_E_NULL_VALUE; + } + + msg_req = qdf_mem_malloc(sizeof(*msg_req)); + if (!msg_req) + return QDF_STATUS_E_NULL_VALUE; + + msg_req->responder = req->responder; + msg_req->vdev = req->vdev; + qdf_mem_copy(msg_req->peer_mac, req->peer_mac, QDF_MAC_ADDR_SIZE); + + msg.bodyptr = msg_req; + msg.callback = tdls_process_cmd; + msg.flush_callback = ucfg_tdls_post_msg_flush_cb; + msg.type = TDLS_CMD_SET_RESPONDER; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("failed to post msg, status %d", status); + qdf_mem_free(msg_req); + } + + return status; +} + +QDF_STATUS ucfg_tdls_teardown_links(struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status; + struct scheduler_msg msg = {0, }; + + if (!vdev) { + tdls_err("vdev is NULL "); + return QDF_STATUS_E_NULL_VALUE; + } + tdls_debug("Enter "); + + msg.bodyptr = vdev; + msg.callback = tdls_process_cmd; + msg.flush_callback = ucfg_tdls_post_msg_flush_cb; + msg.type = TDLS_CMD_TEARDOWN_LINKS; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + + tdls_debug("Exit "); + return status; +} + +QDF_STATUS ucfg_tdls_notify_reset_adapter(struct wlan_objmgr_vdev *vdev) +{ + QDF_STATUS status; + struct scheduler_msg msg = {0, }; + + if (!vdev) { + tdls_err("vdev is NULL "); + return QDF_STATUS_E_NULL_VALUE; + } + tdls_debug("Enter "); + msg.bodyptr = vdev; + msg.callback = tdls_process_cmd; + msg.flush_callback = ucfg_tdls_post_msg_flush_cb; + msg.type = TDLS_NOTIFY_RESET_ADAPTERS; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + return status; +} + +QDF_STATUS ucfg_tdls_notify_sta_connect( + struct tdls_sta_notify_params *notify_info) +{ + struct scheduler_msg msg = {0, }; + struct tdls_sta_notify_params *notify; + QDF_STATUS status; + + if (!notify_info || !notify_info->vdev) { + tdls_err("notify_info %pK", notify_info); + return QDF_STATUS_E_NULL_VALUE; + } + tdls_debug("Enter "); + + notify = qdf_mem_malloc(sizeof(*notify)); + if (!notify) { + wlan_objmgr_vdev_release_ref(notify_info->vdev, + WLAN_TDLS_NB_ID); + return QDF_STATUS_E_NULL_VALUE; + } + + *notify = *notify_info; + + msg.bodyptr = notify; + msg.callback = tdls_process_cmd; + msg.type = TDLS_NOTIFY_STA_CONNECTION; + msg.flush_callback = ucfg_tdls_post_msg_flush_cb; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("failed to post message, status %d", status); + wlan_objmgr_vdev_release_ref(notify->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(notify); + } + + tdls_debug("Exit "); + return status; +} + +QDF_STATUS ucfg_tdls_notify_sta_disconnect( + struct tdls_sta_notify_params *notify_info) +{ + struct scheduler_msg msg = {0, }; + struct tdls_sta_notify_params *notify; + QDF_STATUS status; + + if (!notify_info || !notify_info->vdev) { + tdls_err("notify_info %pK", notify_info); + return QDF_STATUS_E_NULL_VALUE; + } + + tdls_debug("Enter "); + + notify = qdf_mem_malloc(sizeof(*notify)); + if (!notify) + return QDF_STATUS_E_NULL_VALUE; + + *notify = *notify_info; + + msg.bodyptr = notify; + msg.callback = tdls_process_cmd; + msg.type = TDLS_NOTIFY_STA_DISCONNECTION; + msg.flush_callback = ucfg_tdls_post_msg_flush_cb; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("failed to post message, status %d", status); + wlan_objmgr_vdev_release_ref(notify->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(notify); + } + + tdls_debug("Exit "); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_tdls_set_operating_mode( + struct tdls_set_mode_params *set_mode_params) +{ + struct scheduler_msg msg = {0, }; + struct tdls_set_mode_params *set_mode; + QDF_STATUS status; + + if (!set_mode_params || !set_mode_params->vdev) { + tdls_err("set_mode_params %pK", set_mode_params); + return QDF_STATUS_E_NULL_VALUE; + } + + tdls_debug("Enter "); + + set_mode = qdf_mem_malloc(sizeof(*set_mode)); + if (!set_mode) { + tdls_err("memory allocate fail"); + return QDF_STATUS_E_NULL_VALUE; + } + + status = wlan_objmgr_vdev_try_get_ref(set_mode->vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("failed to get vdev ref"); + qdf_mem_free(set_mode); + return status; + } + + set_mode->source = set_mode_params->source; + set_mode->tdls_mode = set_mode_params->tdls_mode; + set_mode->update_last = set_mode_params->update_last; + set_mode->vdev = set_mode_params->vdev; + + msg.bodyptr = set_mode; + msg.callback = tdls_process_cmd; + msg.type = TDLS_CMD_SET_TDLS_MODE; + msg.flush_callback = ucfg_tdls_post_msg_flush_cb; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + wlan_objmgr_vdev_release_ref(set_mode->vdev, WLAN_TDLS_NB_ID); + qdf_mem_free(set_mode); + } + + tdls_debug("Exit "); + + return QDF_STATUS_SUCCESS; +} + +void ucfg_tdls_update_rx_pkt_cnt(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *mac_addr) +{ + QDF_STATUS status; + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (status != QDF_STATUS_SUCCESS) + return; + tdls_update_rx_pkt_cnt(vdev, mac_addr); + + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); +} + +void ucfg_tdls_update_tx_pkt_cnt(struct wlan_objmgr_vdev *vdev, + struct qdf_mac_addr *mac_addr) +{ + QDF_STATUS status; + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (status != QDF_STATUS_SUCCESS) + return; + tdls_update_tx_pkt_cnt(vdev, mac_addr); + + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + +} + +QDF_STATUS ucfg_tdls_antenna_switch(struct wlan_objmgr_vdev *vdev, + uint32_t mode) +{ + QDF_STATUS status; + struct tdls_antenna_switch_request *req; + struct scheduler_msg msg = {0, }; + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + tdls_err("mem allocate fail"); + return QDF_STATUS_E_NOMEM; + } + + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev"); + goto error; + } + + req->vdev = vdev; + req->mode = mode; + + msg.bodyptr = req; + msg.callback = tdls_process_cmd; + msg.flush_callback = tdls_antenna_switch_flush_callback; + msg.type = TDLS_CMD_ANTENNA_SWITCH; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_TDLS, + QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("post antenna switch msg fail"); + goto dec_ref; + } + + return status; + +dec_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); +error: + qdf_mem_free(req); + return status; +} + +QDF_STATUS ucfg_set_tdls_offchannel(struct wlan_objmgr_vdev *vdev, + int offchannel) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct scheduler_msg msg = {0, }; + struct tdls_set_offchannel *req; + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + tdls_err("mem allocate fail"); + return QDF_STATUS_E_NOMEM; + } + + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev"); + goto free; + } + + req->offchannel = offchannel; + req->vdev = vdev; + req->callback = wlan_tdls_offchan_parms_callback; + msg.bodyptr = req; + msg.callback = tdls_process_cmd; + msg.type = TDLS_CMD_SET_OFFCHANNEL; + status = scheduler_post_msg(QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("post set tdls offchannel msg fail"); + goto dec_ref; + } + + return status; + +dec_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + +free: + qdf_mem_free(req); + return status; +} + +QDF_STATUS ucfg_set_tdls_offchan_mode(struct wlan_objmgr_vdev *vdev, + int offchanmode) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct scheduler_msg msg = {0, }; + struct tdls_set_offchanmode *req; + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + tdls_err("mem allocate fail"); + return QDF_STATUS_E_NOMEM; + } + + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev"); + goto free; + } + + req->offchan_mode = offchanmode; + req->vdev = vdev; + req->callback = wlan_tdls_offchan_parms_callback; + msg.bodyptr = req; + msg.callback = tdls_process_cmd; + msg.type = TDLS_CMD_SET_OFFCHANMODE; + status = scheduler_post_msg(QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("post set offchanmode msg fail"); + goto dec_ref; + } + + return status; + +dec_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + +free: + qdf_mem_free(req); + return status; +} + +QDF_STATUS ucfg_set_tdls_secoffchanneloffset(struct wlan_objmgr_vdev *vdev, + int offchanoffset) +{ + int status = QDF_STATUS_SUCCESS; + struct scheduler_msg msg = {0, }; + struct tdls_set_secoffchanneloffset *req; + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + tdls_err("mem allocate fail"); + return QDF_STATUS_E_NOMEM; + } + + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("can't get vdev"); + goto free; + } + + req->offchan_offset = offchanoffset; + req->vdev = vdev; + req->callback = wlan_tdls_offchan_parms_callback; + msg.bodyptr = req; + msg.callback = tdls_process_cmd; + msg.type = TDLS_CMD_SET_SECOFFCHANOFFSET; + status = scheduler_post_msg(QDF_MODULE_ID_OS_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) { + tdls_err("post set secoffchan offset msg fail"); + goto dec_ref; + } + return status; + +dec_ref: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); + +free: + qdf_mem_free(req); + return status; +} diff --git a/components/tdls/dispatcher/src/wlan_tdls_utils_api.c b/components/tdls/dispatcher/src/wlan_tdls_utils_api.c new file mode 100644 index 000000000000..2e9b2ad43ecd --- /dev/null +++ b/components/tdls/dispatcher/src/wlan_tdls_utils_api.c @@ -0,0 +1,23 @@ +/* + * 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_utils_api.c + * + * TDLS utility functions definitions + */ diff --git a/core/cds/inc/cds_config.h b/core/cds/inc/cds_config.h index 4e66afe2e57d..6eb627f0cb46 100644 --- a/core/cds/inc/cds_config.h +++ b/core/cds/inc/cds_config.h @@ -63,7 +63,6 @@ enum active_apf_mode { * @sta_maxlimod_dtim: station max listen interval * @driver_type: Enumeration of Driver Type whether FTM or Mission mode * currently rest of bits are not used - * @dfs_phyerr_filter_offload: DFS Phyerror Filtering offload status from ini * Indicates whether support is enabled or not * @ap_disable_intrabss_fwd: pass intra-bss-fwd info to txrx module * @ap_maxoffload_peers: max offload peer @@ -110,7 +109,6 @@ struct cds_config_info { uint16_t max_bssid; uint8_t sta_maxlimod_dtim; enum qdf_driver_type driver_type; - uint8_t dfs_phyerr_filter_offload; uint8_t ap_maxoffload_peers; uint8_t ap_maxoffload_reorderbuffs; #ifdef FEATURE_WLAN_RA_FILTERING @@ -118,7 +116,6 @@ struct cds_config_info { bool is_ra_ratelimit_enabled; #endif uint8_t reorder_offload; - int32_t dfs_pri_multiplier; uint8_t uc_offload_enabled; uint32_t uc_txbuf_count; uint32_t uc_txbuf_size; diff --git a/core/hdd/inc/wlan_hdd_cfg.h b/core/hdd/inc/wlan_hdd_cfg.h index ab98bda4f0b3..242bf9ad7988 100644 --- a/core/hdd/inc/wlan_hdd_cfg.h +++ b/core/hdd/inc/wlan_hdd_cfg.h @@ -2201,70 +2201,6 @@ enum hdd_link_speed_rpt_type { eHDD_LINK_SPEED_REPORT_MAX_SCALED = 2, }; -/* - * - * gDisableDFSChSwitch - Disable channel switch if radar is found - * @Min: 0 - * @Max: 1 - * @Default: 0 - * - * This ini is used to disable channel switch if radar is found - * on that channel. - * Related: NA. - * - * Supported Feature: DFS - * - * Usage: Internal - * - * - */ -#define CFG_DISABLE_DFS_CH_SWITCH "gDisableDFSChSwitch" -#define CFG_DISABLE_DFS_CH_SWITCH_MIN (0) -#define CFG_DISABLE_DFS_CH_SWITCH_MAX (1) -#define CFG_DISABLE_DFS_CH_SWITCH_DEFAULT (0) - -/* - * - * gDisableDfsJapanW53 - Block W53 channels in random channel selection - * @Min: 0 - * @Max: 1 - * @Default: 0 - * - * This ini is used to block W53 Japan channel in random channel selection - * Related: NA. - * - * Supported Feature: DFS - * - * Usage: Internal/External - * - * - */ -#define CFG_DISABLE_DFS_JAPAN_W53 "gDisableDfsJapanW53" -#define CFG_DISABLE_DFS_JAPAN_W53_MIN (0) -#define CFG_DISABLE_DFS_JAPAN_W53_MAX (1) -#define CFG_DISABLE_DFS_JAPAN_W53_DEFAULT (0) - -/* - * - * dfsPhyerrFilterOffload - DFS PHY error filter offload - * @Min: 0 - * @Max: 1 - * @Default: 0 - * - * This ini is used to enable firmware to do DFS PHY error filtering. - * Related: NA. - * - * Supported Feature: DFS - * - * Usage: Internal/External - * - * - */ -#define CFG_ENABLE_DFS_PHYERR_FILTEROFFLOAD_NAME "dfsPhyerrFilterOffload" -#define CFG_ENABLE_DFS_PHYERR_FILTEROFFLOAD_MIN (0) -#define CFG_ENABLE_DFS_PHYERR_FILTEROFFLOAD_MAX (1) -#define CFG_ENABLE_DFS_PHYERR_FILTEROFFLOAD_DEFAULT (0) - /* * * gReportMaxLinkSpeed - Reporting of max link speed @@ -2936,34 +2872,6 @@ enum hdd_link_speed_rpt_type { #endif /* MSM_PLATFORM */ -/* - * - * gIgnoreCAC - Used to ignore CAC - * @Min: 0 - * @Max: 1 - * @Default: 0 - * - * This ini is used to set default CAC - * - * Related: None - * - * Supported Feature: STA - * - * Usage: Internal/External - * - * - */ - -#define CFG_IGNORE_CAC_NAME "gIgnoreCAC" -#define CFG_IGNORE_CAC_MIN (0) -#define CFG_IGNORE_CAC_MAX (1) -#define CFG_IGNORE_CAC_DEFAULT (0) - -#define CFG_DFS_RADAR_PRI_MULTIPLIER_NAME "gDFSradarMappingPriMultiplier" -#define CFG_DFS_RADAR_PRI_MULTIPLIER_DEFAULT (4) -#define CFG_DFS_RADAR_PRI_MULTIPLIER_MIN (0) -#define CFG_DFS_RADAR_PRI_MULTIPLIER_MAX (10) - #define CFG_ENABLE_SAP_SUSPEND "gEnableSapSuspend" #define CFG_ENABLE_SAP_SUSPEND_MIN (0) #define CFG_ENABLE_SAP_SUSPEND_MAX (1) @@ -3101,11 +3009,6 @@ enum hdd_link_speed_rpt_type { #define CFG_GO_11AC_OVERRIDE_MAX (1) #define CFG_GO_11AC_OVERRIDE_DEFAULT (1) -#define CFG_ENABLE_NON_DFS_CHAN_ON_RADAR "gPreferNonDfsChanOnRadar" -#define CFG_ENABLE_NON_DFS_CHAN_ON_RADAR_MIN (0) -#define CFG_ENABLE_NON_DFS_CHAN_ON_RADAR_MAX (1) -#define CFG_ENABLE_NON_DFS_CHAN_ON_RADAR_DEFAULT (0) - /* * set the self gen power value from * 0 to 0xffff @@ -3115,34 +3018,6 @@ enum hdd_link_speed_rpt_type { #define CFG_SELF_GEN_FRM_PWR_MAX (0xffff) #define CFG_SELF_GEN_FRM_PWR_DEFAULT (0) -/* - * fine timing measurement capability information - * - * <----- fine_time_meas_cap (in bits) -----> - *+----------+-----+-----+------+------+-------+-------+-----+-----+ - *| 8-31 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | - *+----------+-----+-----+------+------+-------+-------+-----+-----+ - *| reserved | SAP | SAP |P2P-GO|P2P-GO|P2P-CLI|P2P-CLI| STA | STA | - *| |resp |init |resp |init |resp |init |resp |init | - *+----------+-----+-----+------+------+-------+-------+-----+-----+ - * - * resp - responder role; init- initiator role - * - * CFG_FINE_TIME_MEAS_CAPABILITY_MAX computed based on the table - * +-----------------+-----------------+-----------+ - * | Device Role | Initiator | Responder | - * +-----------------+-----------------+-----------+ - * | Station | Y | N | - * | P2P-CLI | Y | Y | - * | P2P-GO | Y | Y | - * | SAP | N | Y | - * +-----------------+-----------------+-----------+ - */ -#define CFG_FINE_TIME_MEAS_CAPABILITY "gfine_time_meas_cap" -#define CFG_FINE_TIME_MEAS_CAPABILITY_MIN (0x0000) -#define CFG_FINE_TIME_MEAS_CAPABILITY_MAX (0x00BD) -#define CFG_FINE_TIME_MEAS_CAPABILITY_DEFAULT (0x000D) - /* * * etsi13_srd_chan_in_master_mode - Enable/disable ETSI SRD channels in @@ -3461,16 +3336,6 @@ enum hdd_link_speed_rpt_type { #define CFG_MARK_INDOOR_AS_DISABLE_MAX (1) #define CFG_MARK_INDOOR_AS_DISABLE_DEFAULT (0) -/* - * sap tx leakage threshold - * customer can set this value from 100 to 1000 which means - * sap tx leakage threshold is -10db to -100db - */ -#define CFG_SAP_TX_LEAKAGE_THRESHOLD_NAME "gsap_tx_leakage_threshold" -#define CFG_SAP_TX_LEAKAGE_THRESHOLD_MIN (100) -#define CFG_SAP_TX_LEAKAGE_THRESHOLD_MAX (1000) -#define CFG_SAP_TX_LEAKAGE_THRESHOLD_DEFAULT (310) - /* * * enable_5g_band_pref - Enable preference for 5G from INI. @@ -4231,26 +4096,6 @@ enum hdd_link_speed_rpt_type { #define CFG_ENABLE_PACKET_FILTERS_MAX (63) #endif /* WLAN_FEATURE_PACKET_FILTERING */ -/* - * - * gDfsBeaconTxEnhanced - beacon tx enhanced - * @Min: 0 - * @Max: 1 - * @Default: 0 - * - * This ini is used to enhance dfs beacon tx - * - * Related: none - * - * Usage: External - * - * - */ -#define CFG_DFS_BEACON_TX_ENHANCED "gDfsBeaconTxEnhanced" -#define CFG_DFS_BEACON_TX_ENHANCED_MIN (0) -#define CFG_DFS_BEACON_TX_ENHANCED_MAX (1) -#define CFG_DFS_BEACON_TX_ENHANCED_DEFAULT (0) - /* * * btm_offload_config - Configure BTM @@ -4561,29 +4406,6 @@ enum hdd_link_speed_rpt_type { #define CFG_OFFLOAD_NEIGHBOR_REPORT_MAX_REQ_CAP_MAX (300) #define CFG_OFFLOAD_NEIGHBOR_REPORT_MAX_REQ_CAP_DEFAULT (3) -/* - * - * wmi_wq_watchdog - Sets timeout period for wmi watchdog bite - * @Min: 0 - * @Max: 30 - * @Default: 20 - * - * This ini is used to set timeout period for wmi watchdog bite. If it is - * 0 then wmi watchdog bite is disabled. - * - * Related: None - * - * Supported Feature: STA - * - * Usage: External - * - * - */ -#define CFG_WMI_WQ_WATCHDOG "wmi_wq_watchdog" -#define CFG_WMI_WQ_WATCHDOG_MIN (0) -#define CFG_WMI_WQ_WATCHDOG_MAX (30) /* 30s */ -#define CFG_WMI_WQ_WATCHDOG_DEFAULT (20) /* 20s */ - /* * * gEnableDTIMSelectionDiversity - Enable/Disable chain @@ -5241,8 +5063,6 @@ struct hdd_config { uint32_t ibssPs1RxChainInAtimEnable; uint32_t IpaConfig; bool IpaClkScalingEnable; - uint8_t disableDFSChSwitch; - bool fDfsPhyerrFilterOffload; uint8_t gDisableDfsJapanW53; bool gEnableOverLapCh; bool fRegChangeDefCountry; @@ -5250,12 +5070,8 @@ struct hdd_config { uint8_t enableFwModuleLogLevel[FW_MODULE_LOG_LEVEL_STRING_LENGTH]; - uint8_t ignoreCAC; - bool enable_sap_mandatory_chan_list; - int32_t dfsRadarPriMultiplier; - #ifdef FEATURE_WLAN_FORCE_SAP_SCC uint8_t SapSccChanAvoidance; #endif /* FEATURE_WLAN_FORCE_SAP_SCC */ @@ -5273,9 +5089,6 @@ struct hdd_config { #endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ uint8_t sap_11ac_override; uint8_t go_11ac_override; - uint8_t prefer_non_dfs_on_radar; - /* parameter for defer timer for enabling TDLS on p2p listen */ - uint32_t fine_time_meas_cap; uint8_t max_scan_count; bool etsi13_srd_chan_in_master_mode; uint32_t dual_mac_feature_disable; @@ -5367,7 +5180,6 @@ struct hdd_config { #ifdef WLAN_FEATURE_PACKET_FILTERING uint8_t packet_filters_bitmap; #endif - uint8_t dfs_beacon_tx_enhanced; uint32_t btm_offload_config; #ifdef WLAN_FEATURE_SAE bool is_sae_enabled; @@ -5388,7 +5200,6 @@ struct hdd_config { uint32_t neighbor_report_offload_max_req_cap; bool action_oui_enable; uint8_t action_oui_str[ACTION_OUI_MAXIMUM_ID][ACTION_OUI_MAX_STR_LEN]; - uint16_t wmi_wq_watchdog_timeout; uint8_t enable_tx_sch_delay; uint32_t enable_secondary_rate; bool is_unit_test_framework_enabled; diff --git a/core/hdd/src/wlan_hdd_cfg.c b/core/hdd/src/wlan_hdd_cfg.c index ea772992f7e3..c43d904e28c4 100644 --- a/core/hdd/src/wlan_hdd_cfg.c +++ b/core/hdd/src/wlan_hdd_cfg.c @@ -48,14 +48,6 @@ #include "cfg_ucfg_api.h" #include "hdd_dp_cfg.h" -static void ch_notify_set_g_disable_dfs_japan_w53(struct hdd_context *hdd_ctx, - unsigned long notify_id) -{ - bool disabled = hdd_ctx->config->gDisableDfsJapanW53; - - wlansap_set_dfs_restrict_japan_w53(hdd_ctx->mac_handle, disabled); -} - struct reg_table_entry g_registry_table[] = { REG_VARIABLE(CFG_ENABLE_CONNECTED_SCAN_NAME, WLAN_PARAM_Integer, struct hdd_config, enable_connected_scan, @@ -495,22 +487,6 @@ struct reg_table_entry g_registry_table[] = { CFG_THROTTLE_DUTY_CYCLE_LEVEL3_MIN, CFG_THROTTLE_DUTY_CYCLE_LEVEL3_MAX), - REG_VARIABLE(CFG_DISABLE_DFS_CH_SWITCH, WLAN_PARAM_Integer, - struct hdd_config, disableDFSChSwitch, - VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT, - CFG_DISABLE_DFS_CH_SWITCH_DEFAULT, - CFG_DISABLE_DFS_CH_SWITCH_MIN, - CFG_DISABLE_DFS_CH_SWITCH_MAX), - - REG_DYNAMIC_VARIABLE(CFG_DISABLE_DFS_JAPAN_W53, WLAN_PARAM_Integer, - struct hdd_config, gDisableDfsJapanW53, - VAR_FLAGS_OPTIONAL | - VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT, - CFG_DISABLE_DFS_JAPAN_W53_DEFAULT, - CFG_DISABLE_DFS_JAPAN_W53_MIN, - CFG_DISABLE_DFS_JAPAN_W53_MAX, - ch_notify_set_g_disable_dfs_japan_w53, 0), - REG_VARIABLE(CFG_ENABLE_FIRST_SCAN_2G_ONLY_NAME, WLAN_PARAM_Integer, struct hdd_config, enableFirstScan2GOnly, VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT, @@ -640,14 +616,6 @@ struct reg_table_entry g_registry_table[] = { CFG_IBSS_PS_1RX_CHAIN_IN_ATIM_WINDOW_MIN, CFG_IBSS_PS_1RX_CHAIN_IN_ATIM_WINDOW_MAX), - REG_VARIABLE(CFG_ENABLE_DFS_PHYERR_FILTEROFFLOAD_NAME, - WLAN_PARAM_Integer, - struct hdd_config, fDfsPhyerrFilterOffload, - VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT, - CFG_ENABLE_DFS_PHYERR_FILTEROFFLOAD_DEFAULT, - CFG_ENABLE_DFS_PHYERR_FILTEROFFLOAD_MIN, - CFG_ENABLE_DFS_PHYERR_FILTEROFFLOAD_MAX), - REG_VARIABLE(CFG_ENABLE_OVERLAP_CH, WLAN_PARAM_Integer, struct hdd_config, gEnableOverLapCh, VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK, @@ -698,20 +666,6 @@ struct reg_table_entry g_registry_table[] = { VAR_FLAGS_OPTIONAL, (void *)CFG_ENABLE_FW_MODULE_LOG_DEFAULT), - REG_VARIABLE(CFG_IGNORE_CAC_NAME, WLAN_PARAM_Integer, - struct hdd_config, ignoreCAC, - VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT, - CFG_IGNORE_CAC_DEFAULT, - CFG_IGNORE_CAC_MIN, - CFG_IGNORE_CAC_MAX), - - REG_VARIABLE(CFG_DFS_RADAR_PRI_MULTIPLIER_NAME, WLAN_PARAM_Integer, - struct hdd_config, dfsRadarPriMultiplier, - VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT, - CFG_DFS_RADAR_PRI_MULTIPLIER_DEFAULT, - CFG_DFS_RADAR_PRI_MULTIPLIER_MIN, - CFG_DFS_RADAR_PRI_MULTIPLIER_MAX), - REG_VARIABLE(CFG_ENABLE_SAP_SUSPEND, WLAN_PARAM_Integer, struct hdd_config, enable_sap_suspend, VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT, @@ -773,20 +727,6 @@ struct reg_table_entry g_registry_table[] = { CFG_GO_11AC_OVERRIDE_MIN, CFG_GO_11AC_OVERRIDE_MAX), - REG_VARIABLE(CFG_ENABLE_NON_DFS_CHAN_ON_RADAR, WLAN_PARAM_Integer, - struct hdd_config, prefer_non_dfs_on_radar, - VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT, - CFG_ENABLE_NON_DFS_CHAN_ON_RADAR_DEFAULT, - CFG_ENABLE_NON_DFS_CHAN_ON_RADAR_MIN, - CFG_ENABLE_NON_DFS_CHAN_ON_RADAR_MAX), - - REG_VARIABLE(CFG_FINE_TIME_MEAS_CAPABILITY, WLAN_PARAM_HexInteger, - struct hdd_config, fine_time_meas_cap, - VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT, - CFG_FINE_TIME_MEAS_CAPABILITY_DEFAULT, - CFG_FINE_TIME_MEAS_CAPABILITY_MIN, - CFG_FINE_TIME_MEAS_CAPABILITY_MAX), - REG_VARIABLE(CFG_MAX_SCAN_COUNT_NAME, WLAN_PARAM_Integer, struct hdd_config, max_scan_count, VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT, @@ -996,14 +936,6 @@ struct reg_table_entry g_registry_table[] = { CFG_MARK_INDOOR_AS_DISABLE_MIN, CFG_MARK_INDOOR_AS_DISABLE_MAX), - REG_VARIABLE(CFG_SAP_TX_LEAKAGE_THRESHOLD_NAME, - WLAN_PARAM_Integer, - struct hdd_config, sap_tx_leakage_threshold, - VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT, - CFG_SAP_TX_LEAKAGE_THRESHOLD_DEFAULT, - CFG_SAP_TX_LEAKAGE_THRESHOLD_MIN, - CFG_SAP_TX_LEAKAGE_THRESHOLD_MAX), - REG_VARIABLE(CFG_RESTART_BEACONING_ON_CH_AVOID_NAME, WLAN_PARAM_Integer, struct hdd_config, restart_beaconing_on_chan_avoid_event, VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT, @@ -1223,14 +1155,6 @@ struct reg_table_entry g_registry_table[] = { CFG_LATENCY_FLAGS_ULTRALOW_MIN, CFG_LATENCY_FLAGS_ULTRALOW_MAX), - - REG_VARIABLE(CFG_DFS_BEACON_TX_ENHANCED, WLAN_PARAM_Integer, - struct hdd_config, dfs_beacon_tx_enhanced, - VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT, - CFG_DFS_BEACON_TX_ENHANCED_DEFAULT, - CFG_DFS_BEACON_TX_ENHANCED_MIN, - CFG_DFS_BEACON_TX_ENHANCED_MAX), - REG_VARIABLE(CFG_BTM_ENABLE_NAME, WLAN_PARAM_HexInteger, struct hdd_config, btm_offload_config, VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT, @@ -1341,13 +1265,6 @@ struct reg_table_entry g_registry_table[] = { CFG_OFFLOAD_NEIGHBOR_REPORT_MAX_REQ_CAP_MIN, CFG_OFFLOAD_NEIGHBOR_REPORT_MAX_REQ_CAP_MAX), - REG_VARIABLE(CFG_WMI_WQ_WATCHDOG, WLAN_PARAM_Integer, - struct hdd_config, wmi_wq_watchdog_timeout, - VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT, - CFG_WMI_WQ_WATCHDOG_DEFAULT, - CFG_WMI_WQ_WATCHDOG_MIN, - CFG_WMI_WQ_WATCHDOG_MAX), - REG_VARIABLE(CFG_DTIM_SELECTION_DIVERSITY_NAME, WLAN_PARAM_Integer, struct hdd_config, enable_dtim_selection_diversity, @@ -2540,16 +2457,11 @@ QDF_STATUS hdd_set_idle_ps_config(struct hdd_context *hdd_ctx, bool val) */ static void hdd_set_fine_time_meas_cap(struct hdd_context *hdd_ctx) { - struct hdd_config *config = hdd_ctx->config; - uint32_t capability = config->fine_time_meas_cap; + uint32_t capability = 0; - /* Make sure only supported capabilities are enabled in INI */ - capability &= CFG_FINE_TIME_MEAS_CAPABILITY_MAX; + ucfg_mlme_get_fine_time_meas_cap(hdd_ctx->psoc, &capability); ucfg_wifi_pos_set_ftm_cap(hdd_ctx->psoc, capability); - - hdd_debug("fine time meas capability - INI: %04x Enabled: %04x", - config->fine_time_meas_cap, - capability); + hdd_debug("fine time meas capability - Enabled: %04x", capability); } /** @@ -3029,9 +2941,6 @@ QDF_STATUS hdd_set_sme_config(struct hdd_context *hdd_ctx) hdd_ctx->config->sap_channel_avoidance; #endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ - smeConfig->csrConfig.f_prefer_non_dfs_on_radar = - hdd_ctx->config->prefer_non_dfs_on_radar; - smeConfig->csrConfig.is_ps_enabled = hdd_ctx->config->is_ps_enabled; smeConfig->csrConfig.auto_bmps_timer_val = hdd_ctx->config->auto_bmps_timer_val; diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c index 6aa0e5e51527..6b0e24e826b0 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.c +++ b/core/hdd/src/wlan_hdd_cfg80211.c @@ -5589,6 +5589,7 @@ __wlan_hdd_cfg80211_wifi_configuration_set(struct wiphy *wiphy, uint16_t latency_level; mac_handle_t mac_handle; bool b_value; + uint32_t fine_time_meas_cap = 0; struct wlan_objmgr_vdev *vdev; uint8_t bmiss_first_bcnt; uint8_t bmiss_final_bcnt; @@ -5616,16 +5617,26 @@ __wlan_hdd_cfg80211_wifi_configuration_set(struct wiphy *wiphy, if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT]) { ftm_capab = nla_get_u32(tb[ QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT]); - hdd_ctx->config->fine_time_meas_cap = + fine_time_meas_cap = hdd_ctx->fine_time_meas_cap_target & ftm_capab; + + qdf_status = + ucfg_mlme_set_fine_time_meas_cap(hdd_ctx->psoc, + fine_time_meas_cap); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + hdd_err("FTM capability: values 0x%x, 0x%x, 0x%x", + ftm_capab, hdd_ctx->fine_time_meas_cap_target, + fine_time_meas_cap); + return -EINVAL; + } + sme_update_fine_time_measurement_capab(mac_handle, - adapter->session_id, - hdd_ctx->config->fine_time_meas_cap); - ucfg_wifi_pos_set_ftm_cap(hdd_ctx->psoc, - hdd_ctx->config->fine_time_meas_cap); + adapter->session_id, + fine_time_meas_cap); + ucfg_wifi_pos_set_ftm_cap(hdd_ctx->psoc, fine_time_meas_cap); hdd_debug("FTM capability: user value: 0x%x, target value: 0x%x, final value: 0x%x", - ftm_capab, hdd_ctx->fine_time_meas_cap_target, - hdd_ctx->config->fine_time_meas_cap); + ftm_capab, hdd_ctx->fine_time_meas_cap_target, + fine_time_meas_cap); } if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MODULATED_DTIM]) { diff --git a/core/hdd/src/wlan_hdd_hostapd.c b/core/hdd/src/wlan_hdd_hostapd.c index 3e1b6c66e673..2e115e71201f 100644 --- a/core/hdd/src/wlan_hdd_hostapd.c +++ b/core/hdd/src/wlan_hdd_hostapd.c @@ -4628,10 +4628,11 @@ int wlan_hdd_cfg80211_start_bss(struct hdd_adapter *adapter, uint16_t prev_rsn_length = 0; enum dfs_mode mode; struct hdd_adapter *sta_adapter; - uint8_t ignore_cac = 0; + bool ignore_cac = 0; uint8_t beacon_fixed_len; int value; bool val; + uint32_t tx_leakage_threshold = 0; uint32_t auto_channel_select_weight = cfg_default(CFG_AUTO_CHANNEL_SELECT_WEIGHT); uint8_t pref_chan_location = 0; @@ -4768,7 +4769,6 @@ int wlan_hdd_cfg80211_start_bss(struct hdd_adapter *adapter, hdd_err("ucfg_mlme_get_auto_channel_weight failed, set def"); pConfig->auto_channel_select_weight = auto_channel_select_weight; - pConfig->disableDFSChSwitch = iniConfig->disableDFSChSwitch; ucfg_mlme_get_sap_chn_switch_bcn_count(hdd_ctx->psoc, &value); pConfig->sap_chanswitch_beacon_cnt = value; ucfg_mlme_get_sap_channel_switch_mode(hdd_ctx->psoc, &val); @@ -4793,7 +4793,6 @@ int wlan_hdd_cfg80211_start_bss(struct hdd_adapter *adapter, ucfg_mlme_get_sap_reduces_beacon_interval(hdd_ctx->psoc, &value); pConfig->dtim_period = pBeacon->dtim_period; - pConfig->dfs_beacon_tx_enhanced = iniConfig->dfs_beacon_tx_enhanced; pConfig->reduced_beacon_interval = value; hdd_debug("acs_mode %d", pConfig->acs_cfg.acs_mode); @@ -4866,14 +4865,13 @@ int wlan_hdd_cfg80211_start_bss(struct hdd_adapter *adapter, goto error; } - if (iniConfig->ignoreCAC || + ucfg_mlme_get_dfs_ignore_cac(hdd_ctx->psoc, &ignore_cac); + if (ignore_cac || ((mcc_to_scc_switch != QDF_MCC_TO_SCC_SWITCH_DISABLE) && iniConfig->sta_sap_scc_on_dfs_chan)) ignore_cac = 1; wlansap_set_dfs_ignore_cac(mac_handle, ignore_cac); - wlansap_set_dfs_restrict_japan_w53(mac_handle, - iniConfig->gDisableDfsJapanW53); ucfg_mlme_get_pref_chan_location(hdd_ctx->psoc, &pref_chan_location); wlansap_set_dfs_preferred_channel_location(mac_handle, @@ -4890,8 +4888,9 @@ int wlan_hdd_cfg80211_start_bss(struct hdd_adapter *adapter, pConfig->ieee80211d = 0; } - wlansap_set_tx_leakage_threshold(mac_handle, - iniConfig->sap_tx_leakage_threshold); + ucfg_mlme_get_sap_tx_leakage_threshold(hdd_ctx->psoc, + &tx_leakage_threshold); + tgt_dfs_set_tx_leakage_threshold(hdd_ctx->pdev, tx_leakage_threshold); capab_info = pMgmt_frame->u.beacon.capab_info; diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c index 7f5b17d330ee..9c3462c74374 100644 --- a/core/hdd/src/wlan_hdd_main.c +++ b/core/hdd/src/wlan_hdd_main.c @@ -1885,6 +1885,7 @@ void hdd_update_tgt_cfg(hdd_handle_t hdd_handle, struct wma_tgt_cfg *cfg) mac_handle_t mac_handle; bool bval = false; uint8_t value = 0; + uint32_t fine_time_meas_cap = 0; if (!hdd_ctx) { hdd_err("HDD context is NULL"); @@ -2051,10 +2052,19 @@ void hdd_update_tgt_cfg(hdd_handle_t hdd_handle, struct wma_tgt_cfg *cfg) hdd_update_hw_dbs_capable(hdd_ctx); hdd_ctx->dynamic_nss_chains_support = cfg->dynamic_nss_chains_support; - hdd_ctx->config->fine_time_meas_cap &= cfg->fine_time_measurement_cap; + ucfg_mlme_get_fine_time_meas_cap(hdd_ctx->psoc, &fine_time_meas_cap); + fine_time_meas_cap &= cfg->fine_time_measurement_cap; + status = ucfg_mlme_set_fine_time_meas_cap(hdd_ctx->psoc, + fine_time_meas_cap); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("failed to set fine_time_meas_cap, 0x%x, ox%x", + fine_time_meas_cap, cfg->fine_time_measurement_cap); + ucfg_mlme_get_fine_time_meas_cap(hdd_ctx->psoc, + &fine_time_meas_cap); + } + hdd_ctx->fine_time_meas_cap_target = cfg->fine_time_measurement_cap; - hdd_debug("fine_time_meas_cap: 0x%x", - hdd_ctx->config->fine_time_meas_cap); + hdd_debug("fine_time_meas_cap: 0x%x", fine_time_meas_cap); status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc, &bval); if (!QDF_IS_STATUS_SUCCESS(status)) @@ -2126,15 +2136,18 @@ bool hdd_dfs_indicate_radar(struct hdd_context *hdd_ctx) { struct hdd_adapter *adapter; struct hdd_ap_ctx *ap_ctx; + bool dfs_disable_channel_switch = false; if (!hdd_ctx) { hdd_info("Couldn't get hdd_ctx"); return true; } - if (hdd_ctx->config->disableDFSChSwitch) { + ucfg_mlme_get_dfs_disable_channel_switch(hdd_ctx->psoc, + &dfs_disable_channel_switch); + if (dfs_disable_channel_switch) { hdd_info("skip tx block hdd_ctx=%pK, disableDFSChSwitch=%d", - hdd_ctx, hdd_ctx->config->disableDFSChSwitch); + hdd_ctx, dfs_disable_channel_switch); return true; } @@ -9577,6 +9590,7 @@ int hdd_start_ap_adapter(struct hdd_adapter *adapter) bool is_ssr = false; int ret; struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter); + uint32_t fine_time_meas_cap = 0; hdd_enter(); @@ -9606,12 +9620,14 @@ int hdd_start_ap_adapter(struct hdd_adapter *adapter) return ret; } - if (adapter->device_mode == QDF_SAP_MODE) + if (adapter->device_mode == QDF_SAP_MODE) { + ucfg_mlme_get_fine_time_meas_cap(hdd_ctx->psoc, + &fine_time_meas_cap); sme_cli_set_command(adapter->session_id, WMI_VDEV_PARAM_ENABLE_DISABLE_RTT_RESPONDER_ROLE, - (bool)(hdd_ctx->config->fine_time_meas_cap & - WMI_FW_AP_RTT_RESPR), + (bool)(fine_time_meas_cap & WMI_FW_AP_RTT_RESPR), VDEV_CMD); + } status = hdd_init_ap_mode(adapter, is_ssr); @@ -9712,14 +9728,6 @@ static int hdd_update_cds_config(struct hdd_context *hdd_ctx) ucfg_mlme_get_sap_max_modulated_dtim(hdd_ctx->psoc, &cds_cfg->sta_maxlimod_dtim); - /* - * Copy the DFS Phyerr Filtering Offload status. - * This parameter reflects the value of the - * dfs_phyerr_filter_offload flag as set in the ini. - */ - cds_cfg->dfs_phyerr_filter_offload = - hdd_ctx->config->fDfsPhyerrFilterOffload; - status = ucfg_mlme_get_crash_inject(hdd_ctx->psoc, &crash_inject); if (QDF_IS_STATUS_ERROR(status)) { hdd_err("Failed to get crash inject ini config"); @@ -9754,8 +9762,6 @@ static int hdd_update_cds_config(struct hdd_context *hdd_ctx) &value); cds_cfg->ap_maxoffload_reorderbuffs = value; - cds_cfg->dfs_pri_multiplier = - hdd_ctx->config->dfsRadarPriMultiplier; cds_cfg->reorder_offload = cfg_get(hdd_ctx->psoc, CFG_DP_REORDER_OFFLOAD_SUPPORT); @@ -14229,11 +14235,11 @@ QDF_STATUS hdd_update_score_config( static int hdd_update_dfs_config(struct hdd_context *hdd_ctx) { struct wlan_objmgr_psoc *psoc = hdd_ctx->psoc; - struct hdd_config *cfg = hdd_ctx->config; struct dfs_user_config dfs_cfg; QDF_STATUS status; - dfs_cfg.dfs_is_phyerr_filter_offload = !!cfg->fDfsPhyerrFilterOffload; + ucfg_mlme_get_dfs_filter_offload(hdd_ctx->psoc, + &dfs_cfg.dfs_is_phyerr_filter_offload); status = ucfg_dfs_update_config(psoc, &dfs_cfg); if (QDF_IS_STATUS_ERROR(status)) { hdd_err("failed dfs psoc configuration"); diff --git a/core/mac/inc/ani_global.h b/core/mac/inc/ani_global.h index b80009a417eb..d267aca8b03f 100644 --- a/core/mac/inc/ani_global.h +++ b/core/mac/inc/ani_global.h @@ -834,7 +834,6 @@ struct mac_context { uint8_t beacon_offload; bool pmf_offload; bool is_fils_roaming_supported; - uint8_t f_prefer_non_dfs_on_radar; uint32_t f_sta_miracast_mcc_rest_time_val; #ifdef WLAN_FEATURE_EXTWOW_SUPPORT csr_readyToExtWoWCallback readyToExtWoWCallback; diff --git a/core/mac/inc/qwlan_version.h b/core/mac/inc/qwlan_version.h index da6559829e1c..0144037e5d22 100644 --- a/core/mac/inc/qwlan_version.h +++ b/core/mac/inc/qwlan_version.h @@ -32,9 +32,9 @@ #define QWLAN_VERSION_MAJOR 5 #define QWLAN_VERSION_MINOR 2 #define QWLAN_VERSION_PATCH 0 -#define QWLAN_VERSION_EXTRA "T" +#define QWLAN_VERSION_EXTRA "U" #define QWLAN_VERSION_BUILD 109 -#define QWLAN_VERSIONSTR "5.2.0.109T" +#define QWLAN_VERSIONSTR "5.2.0.109U" #endif /* QWLAN_VERSION_H */ diff --git a/core/mac/src/pe/lim/lim_utils.c b/core/mac/src/pe/lim/lim_utils.c index 5c2a2b5f3342..cd96b2a8dfd1 100644 --- a/core/mac/src/pe/lim/lim_utils.c +++ b/core/mac/src/pe/lim/lim_utils.c @@ -7936,7 +7936,7 @@ void lim_process_ap_ecsa_timeout(void *data) ch = session->gLimChannelSwitch.primaryChannel; ch_width = session->gLimChannelSwitch.ch_width; - if (mac_ctx->sap.SapDfsInfo.dfs_beacon_tx_enhanced) + if (mac_ctx->mlme_cfg->dfs_cfg.dfs_beacon_tx_enhanced) /* Send Action frame after updating beacon */ lim_send_chan_switch_action_frame(mac_ctx, ch, ch_width, session); diff --git a/core/sap/inc/sap_api.h b/core/sap/inc/sap_api.h index 47afecf70343..0794f9fa1773 100644 --- a/core/sap/inc/sap_api.h +++ b/core/sap/inc/sap_api.h @@ -587,7 +587,6 @@ typedef struct sap_config { uint32_t ap_table_expiration_time; uint32_t ht_op_mode_fixed; enum QDF_OPMODE persona; /* Tells us which persona, GO or AP */ - uint8_t disableDFSChSwitch; bool enOverLapCh; #ifdef WLAN_FEATURE_11W bool mfpRequired; @@ -625,7 +624,6 @@ typedef struct sap_config { uint8_t sap_chanswitch_beacon_cnt; uint8_t sap_chanswitch_mode; bool chan_switch_hostapd_rate_enabled; - bool dfs_beacon_tx_enhanced; uint16_t reduced_beacon_interval; } tsap_config_t; @@ -720,7 +718,6 @@ typedef struct sSapDfsInfo { /* beacon count before channel switch */ uint8_t sap_ch_switch_beacon_cnt; uint8_t sap_ch_switch_mode; - bool dfs_beacon_tx_enhanced; uint16_t reduced_beacon_interval; } tSapDfsInfo; @@ -1268,19 +1265,6 @@ QDF_STATUS wlansap_get_dfs_ignore_cac(mac_handle_t mac_handle, QDF_STATUS wlansap_set_dfs_ignore_cac(mac_handle_t mac_handle, uint8_t ignore_cac); -/** - * wlansap_set_dfs_restrict_japan_w53() - enable/disable dfS for japan - * @mac_handle: Opaque handle to the global MAC context - * @disable_dfs_w53: Indicates if Japan W53 is disabled when set to 1 - * Indicates if Japan W53 is enabled when set to 0 - * - * This API is used to enable or disable Japan W53 Band - * Return: The QDF_STATUS code associated with performing the operation - * QDF_STATUS_SUCCESS: Success - */ -QDF_STATUS wlansap_set_dfs_restrict_japan_w53(mac_handle_t mac_handle, - uint8_t disable_dfs_w53); - #ifdef FEATURE_AP_MCC_CH_AVOIDANCE QDF_STATUS wlan_sap_set_channel_avoidance(mac_handle_t mac_handle, @@ -1438,18 +1422,6 @@ QDF_STATUS wlansap_acs_chselect(struct sap_context *sap_context, */ uint32_t wlansap_get_chan_width(struct sap_context *sap_ctx); -/** - * wlansap_set_tx_leakage_threshold() - set sap tx leakage threshold. - * @mac_handle: Opaque handle to the global MAC context - * @tx_leakage_threshold: sap tx leakage threshold - * - * This function set sap tx leakage threshold. - * - * Return: QDF_STATUS. - */ -QDF_STATUS wlansap_set_tx_leakage_threshold(mac_handle_t mac_handle, - uint16_t tx_leakage_threshold); - /* * wlansap_set_invalid_session() - set session ID to invalid * @sap_ctx: pointer to the SAP context diff --git a/core/sap/src/sap_api_link_cntl.c b/core/sap/src/sap_api_link_cntl.c index 74d06117ba1a..96e267147f93 100644 --- a/core/sap/src/sap_api_link_cntl.c +++ b/core/sap/src/sap_api_link_cntl.c @@ -419,7 +419,7 @@ wlansap_roam_process_dfs_chansw_update(mac_handle_t mac_handle, uint8_t dfs_beacon_start_req = 0; bool sap_scc_dfs; - if (sap_ctx->csr_roamProfile.disableDFSChSwitch) { + if (mac_ctx->mlme_cfg->dfs_cfg.dfs_disable_channel_switch) { QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, FL("sapdfs: DFS channel switch disabled")); @@ -582,7 +582,7 @@ wlansap_roam_process_dfs_radar_found(tpAniSirGlobal mac_ctx, tWLAN_SAPEvent sap_event; if (sap_is_dfs_cac_wait_state(sap_ctx)) { - if (sap_ctx->csr_roamProfile.disableDFSChSwitch) { + if (mac_ctx->mlme_cfg->dfs_cfg.dfs_disable_channel_switch) { QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, "sapdfs: DFS channel switch disabled"); diff --git a/core/sap/src/sap_fsm.c b/core/sap/src/sap_fsm.c index 2e65f68f957c..828c6e09b3d6 100644 --- a/core/sap/src/sap_fsm.c +++ b/core/sap/src/sap_fsm.c @@ -193,7 +193,7 @@ static uint8_t sap_random_channel_sel(struct sap_context *sap_ctx) uint8_t ch_wd; struct wlan_objmgr_pdev *pdev = NULL; struct ch_params *ch_params; - uint32_t hw_mode; + uint32_t hw_mode, flag = 0; struct mac_context *mac_ctx; struct dfs_acs_info acs_info = {0}; @@ -226,8 +226,12 @@ static uint8_t sap_random_channel_sel(struct sap_context *sap_ctx) } else { acs_info.acs_mode = false; } + + if (mac_ctx->mlme_cfg->dfs_cfg.dfs_prefer_non_dfs) + flag |= DFS_RANDOM_CH_FLAG_NO_DFS_CH; + if (QDF_IS_STATUS_ERROR(utils_dfs_get_random_channel( - pdev, 0, ch_params, &hw_mode, &ch, &acs_info))) { + pdev, flag, ch_params, &hw_mode, &ch, &acs_info))) { /* No available channel found */ QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, FL("No available channel found!!!")); @@ -544,34 +548,12 @@ void sap_dfs_set_current_channel(void *ctx) tgt_dfs_get_radars(pdev); } tgt_dfs_set_phyerr_filter_offload(pdev); - if (sap_ctx->csr_roamProfile.disableDFSChSwitch) + if (mac_ctx->mlme_cfg->dfs_cfg.dfs_disable_channel_switch) tgt_dfs_control(pdev, DFS_SET_USENOL, &use_nol, sizeof(uint32_t), NULL, NULL, &error); } } -bool sap_dfs_is_w53_invalid(mac_handle_t mac_handle, uint8_t channel_id) -{ - tpAniSirGlobal mac; - - mac = MAC_CONTEXT(mac_handle); - if (NULL == mac) { - QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, - FL("invalid mac")); - return false; - } - - /* - * Check for JAPAN W53 Channel operation capability - */ - if (true == mac->sap.SapDfsInfo.is_dfs_w53_disabled && - true == IS_CHAN_JAPAN_W53(channel_id)) { - return true; - } - - return false; -} - bool sap_dfs_is_channel_in_preferred_location(mac_handle_t mac_handle, uint8_t channel_id) { @@ -2826,7 +2808,6 @@ sapconvert_to_csr_profile(tsap_config_t *pconfig_params, eCsrRoamBssType bssType profile->BSSType = eCSR_BSS_TYPE_INFRA_AP; profile->SSIDs.numOfSSIDs = 1; profile->csrPersona = pconfig_params->persona; - profile->disableDFSChSwitch = pconfig_params->disableDFSChSwitch; qdf_mem_zero(profile->SSIDs.SSIDList[0].SSID.ssId, sizeof(profile->SSIDs.SSIDList[0].SSID.ssId)); @@ -3462,7 +3443,7 @@ uint8_t sap_indicate_radar(struct sap_context *sap_ctx) if (sap_ctx->fsm_state == SAP_STARTED) mac->sap.SapDfsInfo.csaIERequired = true; - if (sap_ctx->csr_roamProfile.disableDFSChSwitch) + if (mac->mlme_cfg->dfs_cfg.dfs_disable_channel_switch) return sap_ctx->channel; /* set the Radar Found flag in SapDfsInfo */ diff --git a/core/sap/src/sap_internal.h b/core/sap/src/sap_internal.h index 764fd4f98a16..a3544eca5f4f 100644 --- a/core/sap/src/sap_internal.h +++ b/core/sap/src/sap_internal.h @@ -387,21 +387,6 @@ bool sap_is_conc_sap_doing_scc_dfs(mac_handle_t mac_handle, uint8_t sap_get_total_number_sap_intf(mac_handle_t mac_handle); -/** - * sap_dfs_is_w53_invalid() - Check to see if invalid W53 operation requested - * @mac_handle: Opaque handle to the global MAC context - * @channel_id: Channel number to be verified - * - * This function checks if the passed channel is W53 and, if so, if - * SAP W53 operation is allowed. - * - * Return: - * * true - operation is invalid because the channel is W53 and W53 - * operation has been disabled - * * false - operation is valid - */ -bool sap_dfs_is_w53_invalid(mac_handle_t mac_handle, uint8_t channel_id); - /** * sap_dfs_is_channel_in_preferred_location() - Verify a channel is valid * with respect to indoor/outdoor location setting diff --git a/core/sap/src/sap_module.c b/core/sap/src/sap_module.c index e9fb64f5adae..54b2468a9846 100644 --- a/core/sap/src/sap_module.c +++ b/core/sap/src/sap_module.c @@ -721,8 +721,6 @@ QDF_STATUS wlansap_start_bss(struct sap_context *sap_ctx, * Copy the DFS Test Mode setting to pmac for * access in lower layers */ - pmac->sap.SapDfsInfo.disable_dfs_ch_switch = - pConfig->disableDFSChSwitch; pmac->sap.SapDfsInfo.sap_ch_switch_beacon_cnt = pConfig->sap_chanswitch_beacon_cnt; pmac->sap.SapDfsInfo.sap_ch_switch_mode = @@ -733,8 +731,6 @@ QDF_STATUS wlansap_start_bss(struct sap_context *sap_ctx, sap_ctx->csr_roamProfile.csrPersona; pmac->sap.sapCtxList[sap_ctx->sessionId].sessionID = sap_ctx->sessionId; - pmac->sap.SapDfsInfo.dfs_beacon_tx_enhanced = - pConfig->dfs_beacon_tx_enhanced; pmac->sap.SapDfsInfo.reduced_beacon_interval = pConfig->reduced_beacon_interval; @@ -1857,47 +1853,6 @@ QDF_STATUS wlansap_set_dfs_ignore_cac(mac_handle_t mac_handle, return QDF_STATUS_SUCCESS; } -QDF_STATUS -wlansap_set_dfs_restrict_japan_w53(mac_handle_t mac_handle, - uint8_t disable_dfs_w53) -{ - tpAniSirGlobal mac = NULL; - QDF_STATUS status; - enum dfs_reg dfs_region; - - if (NULL != mac_handle) { - mac = MAC_CONTEXT(mac_handle); - } else { - QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, - "%s: Invalid mac_handle pointer", __func__); - return QDF_STATUS_E_FAULT; - } - - wlan_reg_get_dfs_region(mac->pdev, &dfs_region); - - /* - * Set the JAPAN W53 restriction only if the current - * regulatory domain is JAPAN. - */ - if (DFS_MKK_REGION == dfs_region) { - mac->sap.SapDfsInfo.is_dfs_w53_disabled = disable_dfs_w53; - QDF_TRACE(QDF_MODULE_ID_SAP, - QDF_TRACE_LEVEL_INFO_LOW, - FL("sapdfs: SET DFS JAPAN W53 DISABLED = %d"), - mac->sap.SapDfsInfo.is_dfs_w53_disabled); - - status = QDF_STATUS_SUCCESS; - } else { - QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, - FL - ("Regdomain not japan, set disable JP W53 not valid")); - - status = QDF_STATUS_E_FAULT; - } - - return status; -} - bool sap_is_auto_channel_select(struct sap_context *sapcontext) { if (NULL == sapcontext) { @@ -2448,25 +2403,6 @@ uint32_t wlansap_get_chan_width(struct sap_context *sap_ctx) return wlan_sap_get_vht_ch_width(sap_ctx); } -QDF_STATUS wlansap_set_tx_leakage_threshold(mac_handle_t mac_handle, - uint16_t tx_leakage_threshold) -{ - tpAniSirGlobal mac; - - if (NULL == mac_handle) { - QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR, - "%s: Invalid mac_handle pointer", __func__); - return QDF_STATUS_E_FAULT; - } - - mac = MAC_CONTEXT(mac_handle); - tgt_dfs_set_tx_leakage_threshold(mac->pdev, tx_leakage_threshold); - QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG, - "%s: leakage_threshold %d", __func__, - tx_leakage_threshold); - return QDF_STATUS_SUCCESS; -} - QDF_STATUS wlansap_set_invalid_session(struct sap_context *sap_ctx) { if (NULL == sap_ctx) { diff --git a/core/sme/inc/csr_api.h b/core/sme/inc/csr_api.h index 8a85c171416d..a40b9233127f 100644 --- a/core/sme/inc/csr_api.h +++ b/core/sme/inc/csr_api.h @@ -880,7 +880,6 @@ struct csr_roam_profile { uint8_t wps_state; tCsrMobilityDomainInfo MDID; enum QDF_OPMODE csrPersona; - uint8_t disableDFSChSwitch; /* addIe params */ tSirAddIeParams addIeParams; uint16_t beacon_tx_rate; @@ -1119,7 +1118,6 @@ typedef struct tagCsrConfigParam { #ifdef FEATURE_AP_MCC_CH_AVOIDANCE bool sap_channel_avoidance; #endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ - uint8_t f_prefer_non_dfs_on_radar; bool is_ps_enabled; uint32_t auto_bmps_timer_val; uint32_t dual_mac_feature_disable; diff --git a/core/sme/src/csr/csr_api_roam.c b/core/sme/src/csr/csr_api_roam.c index 35b1c245d8be..0c14f40d9509 100644 --- a/core/sme/src/csr/csr_api_roam.c +++ b/core/sme/src/csr/csr_api_roam.c @@ -2721,9 +2721,6 @@ QDF_STATUS csr_change_default_config_param(tpAniSirGlobal mac, pParam->sap_channel_avoidance; #endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ - mac->f_prefer_non_dfs_on_radar = - pParam->f_prefer_non_dfs_on_radar; - mac->sme.ps_global_info.ps_enabled = pParam->is_ps_enabled; mac->sme.ps_global_info.auto_bmps_timer_val = @@ -2888,8 +2885,6 @@ QDF_STATUS csr_get_config_param(tpAniSirGlobal mac, tCsrConfigParam *pParam) pParam->sap_channel_avoidance = mac->sap.sap_channel_avoidance; #endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ pParam->max_intf_count = mac->sme.max_intf_count; - pParam->f_prefer_non_dfs_on_radar = - mac->f_prefer_non_dfs_on_radar; pParam->dual_mac_feature_disable = mac->dual_mac_feature_disable; pParam->sta_sap_scc_on_dfs_chan = diff --git a/os_if/p2p/inc/wlan_cfg80211_p2p.h b/os_if/p2p/inc/wlan_cfg80211_p2p.h new file mode 100644 index 000000000000..1c8484b8de4b --- /dev/null +++ b/os_if/p2p/inc/wlan_cfg80211_p2p.h @@ -0,0 +1,115 @@ +/* + * 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: declares P2P functions interfacing with linux kernel + */ + +#ifndef _WLAN_CFG80211_P2P_H_ +#define _WLAN_CFG80211_P2P_H_ + +#include + +struct wlan_objmgr_psoc; +struct wlan_objmgr_vdev; +struct ieee80211_channel; + +/** + * wlan_p2p_start() - start p2p component + * @psoc: soc object + * + * This function used to start P2P component and register events. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS wlan_p2p_start(struct wlan_objmgr_psoc *psoc); + +/** + * wlan_p2p_stop() - stop p2p component + * @psoc: soc object + * + * This function used to stop P2P component and unregister events. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS wlan_p2p_stop(struct wlan_objmgr_psoc *psoc); + +/** + * wlan_cfg80211_roc() - API to process cfg80211 roc request + * @vdev: Pointer to vdev object + * @chan: Pointer to channel + * @duration: Duration for this roc request + * @cookie: Pointer to return cookie to up layer + * + * API to trigger remain on channel request. It returns cookie + * as the identifier of roc. + * + * Return: 0 for success, non zero for failure + */ +int wlan_cfg80211_roc(struct wlan_objmgr_vdev *vdev, + struct ieee80211_channel *chan, uint32_t duration, + uint64_t *cookie); + +/** + * wlan_cfg80211_cancel_roc() - API to process cfg80211 cancel remain + * on channel request + * @vdev: Pointer to vdev object + * @cookie: Find out the roc request by cookie + * + * API to trigger cancel remain on channel request. + * + * Return: 0 for success, non zero for failure + */ +int wlan_cfg80211_cancel_roc(struct wlan_objmgr_vdev *vdev, + uint64_t cookie); + +/** + * wlan_cfg80211_mgmt_tx() - API to process cfg80211 mgmt tx request + * @vdev: Pointer to vdev object + * @chan: Pointer to channel + * @wait: wait time for this mgmt tx request + * @buf: TX buffer + * @len: Length of tx buffer + * @no_cck: Required cck or not + * @dont_wait_for_ack: Wait for ack or not + * @cookie: Return the cookie to caller + * + * API to trigger mgmt frame tx request. It returns cookie as the + * identifier of this tx. + * + * Return: 0 for success, non zero for failure + */ +int wlan_cfg80211_mgmt_tx(struct wlan_objmgr_vdev *vdev, + struct ieee80211_channel *chan, bool offchan, uint32_t wait, + const uint8_t *buf, uint32_t len, bool no_cck, + bool dont_wait_for_ack, uint64_t *cookie); + +/** + * wlan_cfg80211_mgmt_tx_cancel() - API to process cfg80211 cancel to + * wait mgmt tx + * @vdev: Pointer to vdev object + * @cookie: Find out the mgmt tx request by cookie + * + * API to trigger cancel mgmt frame tx request. + * + * Return: 0 for success, non zero for failure + */ +int wlan_cfg80211_mgmt_tx_cancel(struct wlan_objmgr_vdev *vdev, + uint64_t cookie); + +#endif /* _WLAN_CFG80211_P2P_H_ */ diff --git a/os_if/p2p/src/wlan_cfg80211_p2p.c b/os_if/p2p/src/wlan_cfg80211_p2p.c new file mode 100644 index 000000000000..e4911ae8e23f --- /dev/null +++ b/os_if/p2p/src/wlan_cfg80211_p2p.c @@ -0,0 +1,499 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: defines driver functions interfacing with linux kernel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_cfg80211.h" +#include "wlan_cfg80211_p2p.h" + +#define MAX_NO_OF_2_4_CHANNELS 14 +#define MAX_OFFCHAN_TIME_FOR_DNBS 150 + +/** + * wlan_p2p_rx_callback() - Callback for rx mgmt frame + * @user_data: pointer to soc object + * @rx_frame: RX mgmt frame information + * + * This callback will be used to rx frames in os interface. + * + * Return: None + */ +static void wlan_p2p_rx_callback(void *user_data, + struct p2p_rx_mgmt_frame *rx_frame) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_vdev *vdev; + struct vdev_osif_priv *osif_priv; + struct wireless_dev *wdev; + uint16_t freq; + + cfg80211_debug("user data:%pK, vdev id:%d, rssi:%d, buf:%pK, len:%d", + user_data, rx_frame->vdev_id, rx_frame->rx_rssi, + rx_frame->buf, rx_frame->frame_len); + + psoc = user_data; + if (!psoc) { + cfg80211_err("psoc is null"); + return; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + rx_frame->vdev_id, WLAN_P2P_ID); + if (!vdev) { + cfg80211_err("vdev is null"); + return; + } + + osif_priv = wlan_vdev_get_ospriv(vdev); + if (!osif_priv) { + cfg80211_err("osif_priv is null"); + goto fail; + } + + wdev = osif_priv->wdev; + if (!wdev) { + cfg80211_err("wdev is null"); + goto fail; + } + + if (rx_frame->rx_chan <= MAX_NO_OF_2_4_CHANNELS) + freq = ieee80211_channel_to_frequency( + rx_frame->rx_chan, NL80211_BAND_2GHZ); + else + freq = ieee80211_channel_to_frequency( + rx_frame->rx_chan, NL80211_BAND_5GHZ); + + cfg80211_debug("Indicate frame over nl80211, vdev id:%d, idx:%d", + rx_frame->vdev_id, wdev->netdev->ifindex); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)) + cfg80211_rx_mgmt(wdev, freq, rx_frame->rx_rssi * 100, + rx_frame->buf, rx_frame->frame_len, + NL80211_RXMGMT_FLAG_ANSWERED); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) + cfg80211_rx_mgmt(wdev, freq, rx_frame->rx_rssi * 100, + rx_frame->buf, rx_frame->frame_len, + NL80211_RXMGMT_FLAG_ANSWERED, GFP_ATOMIC); +#else + cfg80211_rx_mgmt(wdev, freq, rx_frame->rx_rssi * 100, + rx_frame->buf, rx_frame->frame_len, GFP_ATOMIC); +#endif /* LINUX_VERSION_CODE */ +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); +} + +/** + * wlan_p2p_action_tx_cnf_callback() - Callback for tx confirmation + * @user_data: pointer to soc object + * @tx_cnf: tx confirmation information + * + * This callback will be used to give tx mgmt frame confirmation to + * os interface. + * + * Return: None + */ +static void wlan_p2p_action_tx_cnf_callback(void *user_data, + struct p2p_tx_cnf *tx_cnf) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_vdev *vdev; + struct vdev_osif_priv *osif_priv; + struct wireless_dev *wdev; + bool is_success; + + cfg80211_debug("user data:%pK, action cookie:%llx, buf:%pK, len:%d, tx status:%d", + user_data, tx_cnf->action_cookie, tx_cnf->buf, + tx_cnf->buf_len, tx_cnf->status); + + psoc = user_data; + if (!psoc) { + cfg80211_err("psoc is null"); + return; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + tx_cnf->vdev_id, WLAN_P2P_ID); + if (!vdev) { + cfg80211_err("vdev is null"); + return; + } + + osif_priv = wlan_vdev_get_ospriv(vdev); + if (!osif_priv) { + cfg80211_err("osif_priv is null"); + goto fail; + } + + wdev = osif_priv->wdev; + if (!wdev) { + cfg80211_err("wireless dev is null"); + goto fail; + } + + is_success = tx_cnf->status ? false : true; + cfg80211_mgmt_tx_status( + wdev, + tx_cnf->action_cookie, + tx_cnf->buf, tx_cnf->buf_len, + is_success, GFP_KERNEL); +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); +} + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +/** + * wlan_p2p_lo_event_callback() - Callback for listen offload event + * @user_data: pointer to soc object + * @p2p_lo_event: listen offload event information + * + * This callback will be used to give listen offload event to os interface. + * + * Return: None + */ +static void wlan_p2p_lo_event_callback(void *user_data, + struct p2p_lo_event *p2p_lo_event) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_vdev *vdev; + struct vdev_osif_priv *osif_priv; + struct wireless_dev *wdev; + struct sk_buff *vendor_event; + + cfg80211_debug("user data:%pK, vdev id:%d, reason code:%d", + user_data, p2p_lo_event->vdev_id, + p2p_lo_event->reason_code); + + psoc = user_data; + if (!psoc) { + cfg80211_err("psoc is null"); + return; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + p2p_lo_event->vdev_id, WLAN_P2P_ID); + if (!vdev) { + cfg80211_err("vdev is null"); + return; + } + + osif_priv = wlan_vdev_get_ospriv(vdev); + if (!osif_priv) { + cfg80211_err("osif_priv is null"); + goto fail; + } + + wdev = osif_priv->wdev; + if (!wdev) { + cfg80211_err("wireless dev is null"); + goto fail; + } + + vendor_event = cfg80211_vendor_event_alloc(wdev->wiphy, NULL, + sizeof(uint32_t) + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_P2P_LO_EVENT_INDEX, + GFP_KERNEL); + if (!vendor_event) { + cfg80211_err("cfg80211_vendor_event_alloc failed"); + goto fail; + } + + if (nla_put_u32(vendor_event, + QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON, + p2p_lo_event->reason_code)) { + cfg80211_err("nla put failed"); + kfree_skb(vendor_event); + goto fail; + } + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); + +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); +} + +static inline void wlan_p2p_init_lo_event(struct p2p_start_param *start_param, + struct wlan_objmgr_psoc *psoc) +{ + start_param->lo_event_cb = wlan_p2p_lo_event_callback; + start_param->lo_event_cb_data = psoc; +} +#else +static inline void wlan_p2p_init_lo_event(struct p2p_start_param *start_param, + struct wlan_objmgr_psoc *psoc) +{ +} +#endif /* FEATURE_P2P_LISTEN_OFFLOAD */ +/** + * wlan_p2p_event_callback() - Callback for P2P event + * @user_data: pointer to soc object + * @p2p_event: p2p event information + * + * This callback will be used to give p2p event to os interface. + * + * Return: None + */ +static void wlan_p2p_event_callback(void *user_data, + struct p2p_event *p2p_event) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_vdev *vdev; + struct ieee80211_channel *chan; + struct vdev_osif_priv *osif_priv; + struct wireless_dev *wdev; + + cfg80211_debug("user data:%pK, vdev id:%d, event type:%d", + user_data, p2p_event->vdev_id, p2p_event->roc_event); + + psoc = user_data; + if (!psoc) { + cfg80211_err("psoc is null"); + return; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + p2p_event->vdev_id, WLAN_P2P_ID); + if (!vdev) { + cfg80211_err("vdev is null"); + return; + } + + osif_priv = wlan_vdev_get_ospriv(vdev); + if (!osif_priv) { + cfg80211_err("osif_priv is null"); + goto fail; + } + + wdev = osif_priv->wdev; + if (!wdev) { + cfg80211_err("wireless dev is null"); + goto fail; + } + + chan = ieee80211_get_channel(wdev->wiphy, + wlan_chan_to_freq(p2p_event->chan)); + if (!chan) { + cfg80211_err("channel conversion failed"); + goto fail; + } + + if (p2p_event->roc_event == ROC_EVENT_READY_ON_CHAN) { + cfg80211_ready_on_channel(wdev, + p2p_event->cookie, chan, + p2p_event->duration, GFP_KERNEL); + } else if (p2p_event->roc_event == ROC_EVENT_COMPLETED) { + cfg80211_remain_on_channel_expired(wdev, + p2p_event->cookie, chan, GFP_KERNEL); + } else { + cfg80211_err("Invalid p2p event"); + } + +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); +} + +QDF_STATUS wlan_p2p_start(struct wlan_objmgr_psoc *psoc) +{ + struct p2p_start_param start_param; + + if (!psoc) { + cfg80211_err("psoc null"); + return QDF_STATUS_E_INVAL; + } + + start_param.rx_cb = wlan_p2p_rx_callback; + start_param.rx_cb_data = psoc; + start_param.event_cb = wlan_p2p_event_callback; + start_param.event_cb_data = psoc; + start_param.tx_cnf_cb = wlan_p2p_action_tx_cnf_callback; + start_param.tx_cnf_cb_data = psoc; + wlan_p2p_init_lo_event(&start_param, psoc); + + return ucfg_p2p_psoc_start(psoc, &start_param); +} + +QDF_STATUS wlan_p2p_stop(struct wlan_objmgr_psoc *psoc) +{ + if (!psoc) { + cfg80211_err("psoc null"); + return QDF_STATUS_E_INVAL; + } + + return ucfg_p2p_psoc_stop(psoc); +} + +int wlan_cfg80211_roc(struct wlan_objmgr_vdev *vdev, + struct ieee80211_channel *chan, uint32_t duration, + uint64_t *cookie) +{ + struct p2p_roc_req roc_req = {0}; + struct wlan_objmgr_psoc *psoc; + uint8_t vdev_id; + bool ok; + int ret; + + if (!vdev) { + cfg80211_err("invalid vdev object"); + return -EINVAL; + } + + if (!chan) { + cfg80211_err("invalid channel"); + return -EINVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + cfg80211_err("psoc handle is NULL"); + return -EINVAL; + } + + roc_req.chan = (uint32_t)wlan_freq_to_chan(chan->center_freq); + roc_req.duration = duration; + roc_req.vdev_id = (uint32_t)vdev_id; + + ret = policy_mgr_is_chan_ok_for_dnbs(psoc, roc_req.chan, &ok); + if (QDF_IS_STATUS_ERROR(ret)) { + cfg80211_err("policy_mgr_is_chan_ok_for_dnbs():ret:%d", + ret); + return -EINVAL; + } + + if (!ok) { + cfg80211_err("channel%d not OK for DNBS", roc_req.chan); + return -EINVAL; + } + + return qdf_status_to_os_return( + ucfg_p2p_roc_req(psoc, &roc_req, cookie)); +} + +int wlan_cfg80211_cancel_roc(struct wlan_objmgr_vdev *vdev, + uint64_t cookie) +{ + struct wlan_objmgr_psoc *psoc; + + if (!vdev) { + cfg80211_err("invalid vdev object"); + return -EINVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + cfg80211_err("psoc handle is NULL"); + return -EINVAL; + } + + return qdf_status_to_os_return( + ucfg_p2p_roc_cancel_req(psoc, cookie)); +} + +int wlan_cfg80211_mgmt_tx(struct wlan_objmgr_vdev *vdev, + struct ieee80211_channel *chan, bool offchan, + unsigned int wait, + const uint8_t *buf, uint32_t len, bool no_cck, + bool dont_wait_for_ack, uint64_t *cookie) +{ + struct p2p_mgmt_tx mgmt_tx = {0}; + struct wlan_objmgr_psoc *psoc; + uint8_t vdev_id; + + if (!vdev) { + cfg80211_err("invalid vdev object"); + return -EINVAL; + } + + if (!chan) { + cfg80211_err("invalid channel"); + return -EINVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + vdev_id = wlan_vdev_get_id(vdev); + if (!psoc) { + cfg80211_err("psoc handle is NULL"); + return -EINVAL; + } + + /** + * When offchannel time is more than MAX_OFFCHAN_TIME_FOR_DNBS, + * allow offchannel only if Do_Not_Switch_Channel is not set. + */ + if (wait > MAX_OFFCHAN_TIME_FOR_DNBS) { + int ret; + bool ok; + uint32_t channel = wlan_freq_to_chan(chan->center_freq); + + ret = policy_mgr_is_chan_ok_for_dnbs(psoc, channel, &ok); + if (QDF_IS_STATUS_ERROR(ret)) { + cfg80211_err("policy_mgr_is_chan_ok_for_dnbs():ret:%d", + ret); + return -EINVAL; + } + if (!ok) { + cfg80211_err("Rejecting mgmt_tx for channel:%d as DNSC is set", + channel); + return -EINVAL; + } + } + + mgmt_tx.vdev_id = (uint32_t)vdev_id; + mgmt_tx.chan = (uint32_t)wlan_freq_to_chan(chan->center_freq); + mgmt_tx.wait = wait; + mgmt_tx.len = len; + mgmt_tx.no_cck = (uint32_t)no_cck; + mgmt_tx.dont_wait_for_ack = (uint32_t)dont_wait_for_ack; + mgmt_tx.off_chan = (uint32_t)offchan; + mgmt_tx.buf = buf; + + return qdf_status_to_os_return( + ucfg_p2p_mgmt_tx(psoc, &mgmt_tx, cookie)); +} + +int wlan_cfg80211_mgmt_tx_cancel(struct wlan_objmgr_vdev *vdev, + uint64_t cookie) +{ + struct wlan_objmgr_psoc *psoc; + + if (!vdev) { + cfg80211_err("invalid vdev object"); + return -EINVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + cfg80211_err("psoc handle is NULL"); + return -EINVAL; + } + + return qdf_status_to_os_return( + ucfg_p2p_mgmt_tx_cancel(psoc, vdev, cookie)); +} diff --git a/os_if/tdls/inc/wlan_cfg80211_tdls.h b/os_if/tdls/inc/wlan_cfg80211_tdls.h new file mode 100644 index 000000000000..c4c6b1c6f8ba --- /dev/null +++ b/os_if/tdls/inc/wlan_cfg80211_tdls.h @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: declares driver functions interfacing with linux kernel + */ + +#ifndef _WLAN_CFG80211_TDLS_H_ +#define _WLAN_CFG80211_TDLS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONVERGED_TDLS_ENABLE + +#define TDLS_VDEV_MAGIC 0x54444c53 /* "TDLS" */ + +/** + * struct osif_tdls_vdev - OS tdls vdev private structure + * @tdls_add_peer_comp: Completion to add tdls peer + * @tdls_del_peer_comp: Completion to delete tdls peer + * @tdls_mgmt_comp: Completion to send tdls mgmt packets + * @tdls_link_establish_req_comp: Completion to establish link, sync to + * send establish params to firmware, not used today. + * @tdls_teardown_comp: Completion to teardown tdls peer + * @tdls_user_cmd_comp: tdls user command completion event + * @tdls_antenna_switch_comp: Completion to switch antenna + * @tdls_add_peer_status: Peer status after add peer + * @mgmt_tx_completion_status: Tdls mgmt frames TX completion status code + * @tdls_user_cmd_len: tdls user command written buffer length + * @tdls_antenna_switch_status: return status after antenna switch + */ +struct osif_tdls_vdev { + struct completion tdls_add_peer_comp; + struct completion tdls_del_peer_comp; + struct completion tdls_mgmt_comp; + struct completion tdls_link_establish_req_comp; + struct completion tdls_teardown_comp; + struct completion tdls_user_cmd_comp; + struct completion tdls_antenna_switch_comp; + QDF_STATUS tdls_add_peer_status; + uint32_t mgmt_tx_completion_status; + uint32_t tdls_user_cmd_len; + int tdls_antenna_switch_status; +}; + +/** + * enum qca_wlan_vendor_tdls_trigger_mode_vdev_map: Maps the user space TDLS + * trigger mode in the host driver. + * @WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT: TDLS Connection and + * disconnection handled by user space. + * @WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT: TDLS connection and + * disconnection controlled by host driver based on data traffic. + * @WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL: TDLS connection and + * disconnection jointly controlled by user space and host driver. + */ +enum qca_wlan_vendor_tdls_trigger_mode_vdev_map { + WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT = + QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT, + WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT = + QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT, + WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL = + ((QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT | + QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT) << 1), +}; + +/** + * wlan_cfg80211_tdls_priv_init() - API to initialize tdls os private + * @osif_priv: vdev os private + * + * API to initialize tdls os private + * + * Return: QDF_STATUS + */ +QDF_STATUS wlan_cfg80211_tdls_priv_init(struct vdev_osif_priv *osif_priv); + +/** + * wlan_cfg80211_tdls_priv_deinit() - API to deinitialize tdls os private + * @osif_priv: vdev os private + * + * API to deinitialize tdls os private + * + * Return: None + */ +void wlan_cfg80211_tdls_priv_deinit(struct vdev_osif_priv *osif_priv); + +/** + * wlan_cfg80211_tdls_add_peer() - process cfg80211 add TDLS peer request + * @vdev: vdev object + * @mac: MAC address for TDLS peer + * + * Return: 0 for success; negative errno otherwise + */ +int wlan_cfg80211_tdls_add_peer(struct wlan_objmgr_vdev *vdev, + const uint8_t *mac); + +/** + * wlan_cfg80211_tdls_update_peer() - process cfg80211 update TDLS peer request + * @vdev: vdev object + * @mac: MAC address for TDLS peer + * @params: Pointer to station parameters + * + * Return: 0 for success; negative errno otherwise + */ +int wlan_cfg80211_tdls_update_peer(struct wlan_objmgr_vdev *vdev, + const uint8_t *mac, + struct station_parameters *params); + +/** + * wlan_cfg80211_tdls_configure_mode() - configure tdls mode + * @vdev: vdev obj manager + * @trigger_mode: tdls trgger mode + * + * Return: 0 for success; negative errno otherwise + */ +int wlan_cfg80211_tdls_configure_mode(struct wlan_objmgr_vdev *vdev, + uint32_t trigger_mode); + +/** + * wlan_cfg80211_tdls_oper() - process cfg80211 operation on an TDLS peer + * @vdev: vdev object + * @peer: MAC address of the TDLS peer + * @oper: cfg80211 TDLS operation + * + * Return: 0 on success; negative errno otherwise + */ +int wlan_cfg80211_tdls_oper(struct wlan_objmgr_vdev *vdev, + const uint8_t *peer, + enum nl80211_tdls_operation oper); + +/** + * wlan_cfg80211_tdls_get_all_peers() - get all the TDLS peers from the list + * @vdev: vdev object + * @buf: output buffer + * @buflen: valid length of the output error + * + * Return: length of the output buffer + */ +int wlan_cfg80211_tdls_get_all_peers(struct wlan_objmgr_vdev *vdev, + char *buf, int buflen); + +/** + * wlan_cfg80211_tdls_mgmt() - process tdls management frames from the supplicant + * @vdev: vdev object + * @peer: MAC address of the TDLS peer + * @action_code: type of TDLS mgmt frame to be sent + * @dialog_token: dialog token used in the frame + * @status_code: status to be incuded in the frame + * @peer_capability: peer capability information + * @buf: additional IEs to be included + * @len: length of additional Ies + * @oper: cfg80211 TDLS operation + * + * Return: 0 on success; negative errno otherwise + */ +int wlan_cfg80211_tdls_mgmt(struct wlan_objmgr_vdev *vdev, + const uint8_t *peer, + uint8_t action_code, uint8_t dialog_token, + uint16_t status_code, uint32_t peer_capability, + const uint8_t *buf, size_t len); + +/** + * wlan_tdls_antenna_switch() - process tdls antenna switch + * @vdev: vdev object + * @mode: antenna mode + * + * Return: 0 on success; -EAGAIN to retry + */ +int wlan_tdls_antenna_switch(struct wlan_objmgr_vdev *vdev, uint32_t mode); + +/** + * wlan_cfg80211_tdls_event_callback() - callback for tdls module + * @userdata: user data + * @type: request callback type + * @param: passed parameter + * + * This is used by TDLS to sync with os interface + * + * Return: None + */ +void wlan_cfg80211_tdls_event_callback(void *userdata, + enum tdls_event_type type, + struct tdls_osif_indication *param); + +/** + * wlan_cfg80211_tdls_rx_callback() - Callback for rx mgmt frame + * @user_data: pointer to soc object + * @rx_frame: RX mgmt frame information + * + * This callback will be used to rx frames in os interface. + * + * Return: None + */ +void wlan_cfg80211_tdls_rx_callback(void *user_data, + struct tdls_rx_mgmt_frame *rx_frame); + +/** + * hdd_notify_tdls_reset_adapter() - notify reset adapter to TDLS + * @vdev: vdev object manager + * + * Notify hdd reset adapter to TDLS component + * + * Return: None + */ +void hdd_notify_tdls_reset_adapter(struct wlan_objmgr_vdev *vdev); + +/** + * hdd_notify_sta_connect() - notify sta connect to TDLS + * @session_id: pointer to soc object + * @tdls_chan_swit_prohibited: indicates channel switch capability + * @tdls_prohibited: indicates tdls allowed or not + * @vdev: vdev object manager + * + * Notify sta connect event to TDLS component + * + * Return: None + */ +void +hdd_notify_sta_connect(uint8_t session_id, + bool tdls_chan_swit_prohibited, + bool tdls_prohibited, + struct wlan_objmgr_vdev *vdev); + +/** + * hdd_notify_sta_disconnect() - notify sta disconnect to TDLS + * @session_id: pointer to soc object + * @lfr_roam: indicate, whether disconnect due to lfr roam + * @bool user_disconnect: disconnect from user space + * @vdev: vdev object manager + * + * Notify sta disconnect event to TDLS component + * + * Return: None + */ +void hdd_notify_sta_disconnect(uint8_t session_id, + bool lfr_roam, + bool user_disconnect, + struct wlan_objmgr_vdev *vdev); + +/** + * hdd_notify_teardown_tdls_links() - notify TDLS to teardown links + * @vdev: vdev object manager + * + * Notify tdls to teardown all the links, due to certain events + * in the system + * + * Return: None + */ +void hdd_notify_teardown_tdls_links(struct wlan_objmgr_vdev *vdev); + +#else +static inline void +hdd_notify_tdls_reset_adapter(struct wlan_objmgr_vdev *vdev) +{ +} + +static inline void +hdd_notify_sta_connect(uint8_t session_id, + bool tdls_chan_swit_prohibited, + bool tdls_prohibited, + struct wlan_objmgr_vdev *vdev) +{ +} + +static inline +void hdd_notify_sta_disconnect(uint8_t session_id, + bool lfr_roam, + bool user_disconnect, + struct wlan_objmgr_vdev *vdev) +{ + +} + +static inline +int wlan_cfg80211_tdls_configure_mode(struct wlan_objmgr_vdev *vdev, + uint32_t trigger_mode) +{ + return 0; +} + +static inline +void hdd_notify_teardown_tdls_links(struct wlan_objmgr_vdev *vdev) +{ + +} +#endif +#endif diff --git a/os_if/tdls/src/wlan_cfg80211_tdls.c b/os_if/tdls/src/wlan_cfg80211_tdls.c new file mode 100644 index 000000000000..6a04713f4de5 --- /dev/null +++ b/os_if/tdls/src/wlan_cfg80211_tdls.c @@ -0,0 +1,922 @@ +/* + * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * DOC: defines driver functions interfacing with linux kernel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CHANNEL (NUM_24GHZ_CHANNELS + NUM_5GHZ_CHANNELS) + + +#define TDLS_MAX_NO_OF_2_4_CHANNELS 14 + +static int wlan_cfg80211_tdls_validate_mac_addr(const uint8_t *mac) +{ + static const uint8_t temp_mac[QDF_MAC_ADDR_SIZE] = {0}; + + if (!qdf_mem_cmp(mac, temp_mac, QDF_MAC_ADDR_SIZE)) { + cfg80211_debug("Invalid Mac address " QDF_MAC_ADDR_STR " cmd declined.", + QDF_MAC_ADDR_ARRAY(mac)); + return -EINVAL; + } + + return 0; +} + +QDF_STATUS wlan_cfg80211_tdls_priv_init(struct vdev_osif_priv *osif_priv) +{ + struct osif_tdls_vdev *tdls_priv; + + cfg80211_debug("initialize tdls os if layer private structure"); + tdls_priv = qdf_mem_malloc(sizeof(*tdls_priv)); + if (!tdls_priv) { + cfg80211_err("failed to allocate memory for tdls_priv"); + return QDF_STATUS_E_NOMEM; + } + init_completion(&tdls_priv->tdls_add_peer_comp); + init_completion(&tdls_priv->tdls_del_peer_comp); + init_completion(&tdls_priv->tdls_mgmt_comp); + init_completion(&tdls_priv->tdls_link_establish_req_comp); + init_completion(&tdls_priv->tdls_teardown_comp); + init_completion(&tdls_priv->tdls_user_cmd_comp); + init_completion(&tdls_priv->tdls_antenna_switch_comp); + + osif_priv->osif_tdls = tdls_priv; + + return QDF_STATUS_SUCCESS; +} + +void wlan_cfg80211_tdls_priv_deinit(struct vdev_osif_priv *osif_priv) +{ + cfg80211_debug("deinitialize tdls os if layer private structure"); + if (!osif_priv) { + cfg80211_err("OS private structure of vdev is null "); + return; + } + if (osif_priv->osif_tdls) + qdf_mem_free(osif_priv->osif_tdls); + osif_priv->osif_tdls = NULL; +} + +void hdd_notify_teardown_tdls_links(struct wlan_objmgr_vdev *vdev) +{ + struct vdev_osif_priv *osif_priv; + struct osif_tdls_vdev *tdls_priv; + QDF_STATUS status; + unsigned long rc; + + if (!vdev) + return; + + osif_priv = wlan_vdev_get_ospriv(vdev); + + tdls_priv = osif_priv->osif_tdls; + + reinit_completion(&tdls_priv->tdls_teardown_comp); + status = ucfg_tdls_teardown_links(vdev); + if (QDF_IS_STATUS_ERROR(status)) { + cfg80211_err("ucfg_tdls_teardown_links failed err %d", status); + return; + } + + cfg80211_debug("Wait for tdls teardown completion. Timeout %u ms", + WAIT_TIME_FOR_TDLS_TEARDOWN_LINKS); + + rc = wait_for_completion_timeout( + &tdls_priv->tdls_teardown_comp, + msecs_to_jiffies(WAIT_TIME_FOR_TDLS_TEARDOWN_LINKS)); + + if (0 == rc) { + cfg80211_err(" Teardown Completion timed out rc: %ld", rc); + return; + } + + cfg80211_debug("TDLS teardown completion status %ld ", rc); +} + +void hdd_notify_tdls_reset_adapter(struct wlan_objmgr_vdev *vdev) +{ + ucfg_tdls_notify_reset_adapter(vdev); +} + +void +hdd_notify_sta_connect(uint8_t session_id, + bool tdls_chan_swit_prohibited, + bool tdls_prohibited, + struct wlan_objmgr_vdev *vdev) +{ + struct tdls_sta_notify_params notify_info = {0}; + QDF_STATUS status; + + if (!vdev) { + cfg80211_err("vdev is NULL"); + return; + } + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + cfg80211_err("can't get vdev"); + return; + } + + notify_info.session_id = session_id; + notify_info.vdev = vdev; + notify_info.tdls_chan_swit_prohibited = tdls_chan_swit_prohibited; + notify_info.tdls_prohibited = tdls_prohibited; + ucfg_tdls_notify_sta_connect(¬ify_info); +} + +void hdd_notify_sta_disconnect(uint8_t session_id, + bool lfr_roam, + bool user_disconnect, + struct wlan_objmgr_vdev *vdev) +{ + struct tdls_sta_notify_params notify_info = {0}; + QDF_STATUS status; + + if (!vdev) { + cfg80211_err("vdev is NULL"); + return; + } + + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID); + if (QDF_IS_STATUS_ERROR(status)) { + cfg80211_err("can't get vdev"); + return; + } + + notify_info.session_id = session_id; + notify_info.lfr_roam = lfr_roam; + notify_info.tdls_chan_swit_prohibited = false; + notify_info.tdls_prohibited = false; + notify_info.vdev = vdev; + notify_info.user_disconnect = user_disconnect; + ucfg_tdls_notify_sta_disconnect(¬ify_info); +} + +int wlan_cfg80211_tdls_add_peer(struct wlan_objmgr_vdev *vdev, + const uint8_t *mac) +{ + struct tdls_add_peer_params *add_peer_req; + int status; + struct vdev_osif_priv *osif_priv; + struct osif_tdls_vdev *tdls_priv; + unsigned long rc; + + status = wlan_cfg80211_tdls_validate_mac_addr(mac); + + if (status) + return status; + + cfg80211_debug("Add TDLS peer " QDF_MAC_ADDR_STR, + QDF_MAC_ADDR_ARRAY(mac)); + + add_peer_req = qdf_mem_malloc(sizeof(*add_peer_req)); + if (!add_peer_req) { + cfg80211_err("Failed to allocate tdls add peer request mem"); + return -EINVAL; + } + + osif_priv = wlan_vdev_get_ospriv(vdev); + tdls_priv = osif_priv->osif_tdls; + add_peer_req->vdev_id = wlan_vdev_get_id(vdev); + + qdf_mem_copy(add_peer_req->peer_addr, mac, QDF_MAC_ADDR_SIZE); + + reinit_completion(&tdls_priv->tdls_add_peer_comp); + status = ucfg_tdls_add_peer(vdev, add_peer_req); + if (QDF_IS_STATUS_ERROR(status)) { + cfg80211_err("ucfg_tdls_add_peer returned err %d", status); + status = -EIO; + goto error; + } + + rc = wait_for_completion_timeout( + &tdls_priv->tdls_add_peer_comp, + msecs_to_jiffies(WAIT_TIME_TDLS_ADD_STA)); + if (!rc) { + cfg80211_err("timeout for tdls add peer indication %ld", rc); + status = -EPERM; + goto error; + } + + if (QDF_IS_STATUS_ERROR(tdls_priv->tdls_add_peer_status)) { + cfg80211_err("tdls add peer failed, status:%d", + tdls_priv->tdls_add_peer_status); + status = -EPERM; + } +error: + qdf_mem_free(add_peer_req); + return status; +} + +static bool +is_duplicate_channel(uint8_t *arr, int index, uint8_t match) +{ + int i; + + for (i = 0; i < index; i++) { + if (arr[i] == match) + return true; + } + return false; +} + +static void +tdls_calc_channels_from_staparams(struct tdls_update_peer_params *req_info, + struct station_parameters *params) +{ + int i = 0, j = 0, k = 0, no_of_channels = 0; + int num_unique_channels; + int next; + uint8_t *dest_chans; + const uint8_t *src_chans; + + dest_chans = req_info->supported_channels; + src_chans = params->supported_channels; + + /* Convert (first channel , number of channels) tuple to + * the total list of channels. This goes with the assumption + * that if the first channel is < 14, then the next channels + * are an incremental of 1 else an incremental of 4 till the number + * of channels. + */ + for (i = 0; i < params->supported_channels_len && + j < WLAN_MAC_MAX_SUPP_CHANNELS; i += 2) { + int wifi_chan_index; + + if (!is_duplicate_channel(dest_chans, j, src_chans[i])) + dest_chans[j] = src_chans[i]; + else + continue; + + wifi_chan_index = ((dest_chans[j] <= WLAN_CHANNEL_14) ? 1 : 4); + no_of_channels = src_chans[i + 1]; + + cfg80211_debug("i:%d,j:%d,k:%d,[%d]:%d,index:%d,chans_num: %d", + i, j, k, j, + dest_chans[j], + wifi_chan_index, + no_of_channels); + + for (k = 1; k <= no_of_channels && + j < WLAN_MAC_MAX_SUPP_CHANNELS - 1; k++) { + next = dest_chans[j] + wifi_chan_index; + + if (!is_duplicate_channel(dest_chans, j + 1, next)) + dest_chans[j + 1] = next; + else + continue; + + cfg80211_debug("i: %d, j: %d, k: %d, [%d]: %d", + i, j, k, j + 1, dest_chans[j + 1]); + j += 1; + } + } + num_unique_channels = j + 1; + cfg80211_debug("Unique Channel List: supported_channels "); + for (i = 0; i < num_unique_channels; i++) + cfg80211_debug("[%d]: %d,", i, dest_chans[i]); + + if (MAX_CHANNEL < num_unique_channels) + num_unique_channels = MAX_CHANNEL; + req_info->supported_channels_len = num_unique_channels; + cfg80211_debug("After removing duplcates supported_channels_len: %d", + req_info->supported_channels_len); +} + +static void +wlan_cfg80211_tdls_extract_params(struct tdls_update_peer_params *req_info, + struct station_parameters *params) +{ + int i; + + cfg80211_debug("sta cap %d, uapsd_queue %d, max_sp %d", + params->capability, + params->uapsd_queues, params->max_sp); + + if (!req_info) { + cfg80211_err("reg_info is NULL"); + return; + } + req_info->capability = params->capability; + req_info->uapsd_queues = params->uapsd_queues; + req_info->max_sp = params->max_sp; + + if (params->supported_channels_len) + tdls_calc_channels_from_staparams(req_info, params); + + qdf_mem_copy(req_info->supported_oper_classes, + params->supported_oper_classes, + params->supported_oper_classes_len); + req_info->supported_oper_classes_len = + params->supported_oper_classes_len; + + if (params->ext_capab_len) + qdf_mem_copy(req_info->extn_capability, params->ext_capab, + sizeof(req_info->extn_capability)); + + if (params->ht_capa) { + req_info->htcap_present = 1; + qdf_mem_copy(&req_info->ht_cap, params->ht_capa, + sizeof(struct htcap_cmn_ie)); + } + + req_info->supported_rates_len = params->supported_rates_len; + + /* Note : The Maximum sizeof supported_rates sent by the Supplicant is + * 32. The supported_rates array , for all the structures propogating + * till Add Sta to the firmware has to be modified , if the supplicant + * (ieee80211) is modified to send more rates. + */ + + /* To avoid Data Currption , set to max length to SIR_MAC_MAX_SUPP_RATES + */ + if (req_info->supported_rates_len > WLAN_MAC_MAX_SUPP_RATES) + req_info->supported_rates_len = WLAN_MAC_MAX_SUPP_RATES; + + if (req_info->supported_rates_len) { + qdf_mem_copy(req_info->supported_rates, + params->supported_rates, + req_info->supported_rates_len); + cfg80211_debug("Supported Rates with Length %d", + req_info->supported_rates_len); + + for (i = 0; i < req_info->supported_rates_len; i++) + cfg80211_debug("[%d]: %0x", i, + req_info->supported_rates[i]); + } + + if (params->vht_capa) { + req_info->vhtcap_present = 1; + qdf_mem_copy(&req_info->vht_cap, params->vht_capa, + sizeof(struct vhtcap)); + } + + if (params->ht_capa || params->vht_capa || + (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME))) + req_info->is_qos_wmm_sta = true; +} + +int wlan_cfg80211_tdls_update_peer(struct wlan_objmgr_vdev *vdev, + const uint8_t *mac, + struct station_parameters *params) +{ + struct tdls_update_peer_params *req_info; + int status; + struct vdev_osif_priv *osif_priv; + struct osif_tdls_vdev *tdls_priv; + unsigned long rc; + + status = wlan_cfg80211_tdls_validate_mac_addr(mac); + + if (status) + return status; + + cfg80211_debug("Update TDLS peer " QDF_MAC_ADDR_STR, + QDF_MAC_ADDR_ARRAY(mac)); + + req_info = qdf_mem_malloc(sizeof(*req_info)); + if (!req_info) { + cfg80211_err("Failed to allocate tdls add peer request mem"); + return -EINVAL; + } + wlan_cfg80211_tdls_extract_params(req_info, params); + + osif_priv = wlan_vdev_get_ospriv(vdev); + tdls_priv = osif_priv->osif_tdls; + req_info->vdev_id = wlan_vdev_get_id(vdev); + qdf_mem_copy(req_info->peer_addr, mac, QDF_MAC_ADDR_SIZE); + + reinit_completion(&tdls_priv->tdls_add_peer_comp); + status = ucfg_tdls_update_peer(vdev, req_info); + if (QDF_IS_STATUS_ERROR(status)) { + cfg80211_err("ucfg_tdls_update_peer returned err %d", status); + status = -EIO; + goto error; + } + + rc = wait_for_completion_timeout( + &tdls_priv->tdls_add_peer_comp, + msecs_to_jiffies(WAIT_TIME_TDLS_ADD_STA)); + if (!rc) { + cfg80211_err("timeout for tdls update peer indication %ld", rc); + status = -EPERM; + goto error; + } + + if (QDF_IS_STATUS_ERROR(tdls_priv->tdls_add_peer_status)) { + cfg80211_err("tdls update peer failed, status:%d", + tdls_priv->tdls_add_peer_status); + status = -EPERM; + } +error: + qdf_mem_free(req_info); + return status; +} + +static char *tdls_oper_to_str(enum nl80211_tdls_operation oper) +{ + switch (oper) { + case NL80211_TDLS_ENABLE_LINK: + return "TDLS_ENABLE_LINK"; + case NL80211_TDLS_DISABLE_LINK: + return "TDLS_DISABLE_LINK"; + case NL80211_TDLS_TEARDOWN: + return "TDLS_TEARDOWN"; + case NL80211_TDLS_SETUP: + return "TDLS_SETUP"; + default: + return "UNKNOWN:ERR"; + } +} + +static enum tdls_command_type tdls_oper_to_cmd(enum nl80211_tdls_operation oper) +{ + if (oper == NL80211_TDLS_ENABLE_LINK) + return TDLS_CMD_ENABLE_LINK; + else if (oper == NL80211_TDLS_DISABLE_LINK) + return TDLS_CMD_DISABLE_LINK; + else if (oper == NL80211_TDLS_TEARDOWN) + return TDLS_CMD_REMOVE_FORCE_PEER; + else if (oper == NL80211_TDLS_SETUP) + return TDLS_CMD_CONFIG_FORCE_PEER; + else + return 0; +} + +int wlan_cfg80211_tdls_configure_mode(struct wlan_objmgr_vdev *vdev, + uint32_t trigger_mode) +{ + enum tdls_feature_mode tdls_mode; + struct tdls_set_mode_params set_mode_params; + int status; + + if (!vdev) + return -EINVAL; + + switch (trigger_mode) { + case WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT: + tdls_mode = TDLS_SUPPORT_EXP_TRIG_ONLY; + return 0; + case WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL: + tdls_mode = TDLS_SUPPORT_EXT_CONTROL; + break; + case WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT: + tdls_mode = TDLS_SUPPORT_IMP_MODE; + return 0; + default: + cfg80211_err("Invalid TDLS trigger mode"); + return -EINVAL; + } + + cfg80211_notice("cfg80211 tdls trigger mode %d", trigger_mode); + set_mode_params.source = TDLS_SET_MODE_SOURCE_USER; + set_mode_params.tdls_mode = tdls_mode; + set_mode_params.update_last = false; + set_mode_params.vdev = vdev; + + status = ucfg_tdls_set_operating_mode(&set_mode_params); + return status; +} + +int wlan_cfg80211_tdls_oper(struct wlan_objmgr_vdev *vdev, + const uint8_t *peer, + enum nl80211_tdls_operation oper) +{ + struct vdev_osif_priv *osif_priv; + struct osif_tdls_vdev *tdls_priv; + int status; + unsigned long rc; + enum tdls_command_type cmd; + + status = wlan_cfg80211_tdls_validate_mac_addr(peer); + + if (status) + return status; + + if (NL80211_TDLS_DISCOVERY_REQ == oper) { + cfg80211_warn( + "We don't support in-driver setup/teardown/discovery"); + return -ENOTSUPP; + } + + cfg80211_debug("%s start", tdls_oper_to_str(oper)); + cmd = tdls_oper_to_cmd(oper); + switch (oper) { + case NL80211_TDLS_ENABLE_LINK: + case NL80211_TDLS_TEARDOWN: + case NL80211_TDLS_SETUP: + status = ucfg_tdls_oper(vdev, peer, cmd); + if (QDF_IS_STATUS_ERROR(status)) { + cfg80211_err("%s fail %d", + tdls_oper_to_str(oper), status); + status = -EIO; + goto error; + } + break; + case NL80211_TDLS_DISABLE_LINK: + osif_priv = wlan_vdev_get_ospriv(vdev); + tdls_priv = osif_priv->osif_tdls; + reinit_completion(&tdls_priv->tdls_del_peer_comp); + status = ucfg_tdls_oper(vdev, peer, cmd); + if (QDF_IS_STATUS_ERROR(status)) { + cfg80211_err("ucfg_tdls_disable_link fail %d", status); + status = -EIO; + goto error; + } + + rc = wait_for_completion_timeout( + &tdls_priv->tdls_del_peer_comp, + msecs_to_jiffies(WAIT_TIME_TDLS_DEL_STA)); + if (!rc) { + cfg80211_err("timeout for tdls disable link %ld", rc); + status = -EPERM; + } + break; + default: + cfg80211_err("unsupported event %d", oper); + status = -ENOTSUPP; + } + +error: + return status; +} + +void wlan_cfg80211_tdls_rx_callback(void *user_data, + struct tdls_rx_mgmt_frame *rx_frame) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_objmgr_vdev *vdev; + struct vdev_osif_priv *osif_priv; + struct wireless_dev *wdev; + uint16_t freq; + + cfg80211_debug("user data:%pK, vdev id:%d, rssi:%d, buf:%pK, len:%d", + user_data, rx_frame->vdev_id, rx_frame->rx_rssi, + rx_frame->buf, rx_frame->frame_len); + + psoc = user_data; + if (!psoc) { + cfg80211_err("psoc is null"); + return; + } + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + rx_frame->vdev_id, WLAN_TDLS_NB_ID); + if (!vdev) { + cfg80211_err("vdev is null"); + return; + } + + osif_priv = wlan_vdev_get_ospriv(vdev); + if (!osif_priv) { + cfg80211_err("osif_priv is null"); + goto fail; + } + + wdev = osif_priv->wdev; + if (!wdev) { + cfg80211_err("wdev is null"); + goto fail; + } + + if (rx_frame->rx_chan <= TDLS_MAX_NO_OF_2_4_CHANNELS) + freq = ieee80211_channel_to_frequency( + rx_frame->rx_chan, NL80211_BAND_2GHZ); + else + freq = ieee80211_channel_to_frequency( + rx_frame->rx_chan, NL80211_BAND_5GHZ); + + cfg80211_notice("Indicate frame over nl80211, vdev id:%d, idx:%d", + rx_frame->vdev_id, wdev->netdev->ifindex); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)) + cfg80211_rx_mgmt(wdev, freq, rx_frame->rx_rssi * 100, + rx_frame->buf, rx_frame->frame_len, + NL80211_RXMGMT_FLAG_ANSWERED); +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) + cfg80211_rx_mgmt(wdev, freq, rx_frame->rx_rssi * 100, + rx_frame->buf, rx_frame->frame_len, + NL80211_RXMGMT_FLAG_ANSWERED, GFP_ATOMIC); +#else + cfg80211_rx_mgmt(wdev, freq, rx_frame->rx_rssi * 100, + rx_frame->buf, rx_frame->frame_len, GFP_ATOMIC); +#endif /* LINUX_VERSION_CODE */ +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID); +} + +int wlan_cfg80211_tdls_get_all_peers(struct wlan_objmgr_vdev *vdev, + char *buf, int buflen) +{ + struct vdev_osif_priv *osif_priv; + struct osif_tdls_vdev *tdls_priv; + int32_t len; + QDF_STATUS status; + unsigned long rc; + + osif_priv = wlan_vdev_get_ospriv(vdev); + tdls_priv = osif_priv->osif_tdls; + + reinit_completion(&tdls_priv->tdls_user_cmd_comp); + status = ucfg_tdls_get_all_peers(vdev, buf, buflen); + if (QDF_IS_STATUS_ERROR(status)) { + cfg80211_err("ucfg_tdls_get_all_peers failed err %d", status); + len = scnprintf(buf, buflen, + "\nucfg_tdls_send_mgmt failed\n"); + goto error_get_tdls_peers; + } + + cfg80211_debug("Wait for tdls_user_cmd_comp. Timeout %u ms", + WAIT_TIME_FOR_TDLS_USER_CMD); + + rc = wait_for_completion_timeout( + &tdls_priv->tdls_user_cmd_comp, + msecs_to_jiffies(WAIT_TIME_FOR_TDLS_USER_CMD)); + + if (0 == rc) { + cfg80211_err("TDLS user cmd get all peers timed out rc %ld", + rc); + len = scnprintf(buf, buflen, + "\nTDLS user cmd get all peers timed out\n"); + goto error_get_tdls_peers; + } + + len = tdls_priv->tdls_user_cmd_len; + +error_get_tdls_peers: + return len; +} + +int wlan_cfg80211_tdls_mgmt(struct wlan_objmgr_vdev *vdev, + const uint8_t *peer_mac, + uint8_t action_code, uint8_t dialog_token, + uint16_t status_code, uint32_t peer_capability, + const uint8_t *buf, size_t len) +{ + struct tdls_action_frame_request mgmt_req; + struct vdev_osif_priv *osif_priv; + struct osif_tdls_vdev *tdls_priv; + int status; + unsigned long rc; + int max_sta_failed = 0; + struct tdls_validate_action_req chk_frame; + struct tdls_set_responder_req set_responder; + + status = wlan_cfg80211_tdls_validate_mac_addr(peer_mac); + + if (status) + return status; + + osif_priv = wlan_vdev_get_ospriv(vdev); + + tdls_priv = osif_priv->osif_tdls; + + /* make sure doesn't call send_mgmt() while it is pending */ + if (TDLS_VDEV_MAGIC == tdls_priv->mgmt_tx_completion_status) { + cfg80211_err(QDF_MAC_ADDR_STR " action %d couldn't sent, as one is pending. return EBUSY", + QDF_MAC_ADDR_ARRAY(peer_mac), action_code); + return -EBUSY; + } + + /* Reset TDLS VDEV magic */ + tdls_priv->mgmt_tx_completion_status = TDLS_VDEV_MAGIC; + + + /*prepare the request */ + + /* Validate the management Request */ + chk_frame.vdev = vdev; + chk_frame.action_code = action_code; + qdf_mem_copy(chk_frame.peer_mac, peer_mac, QDF_MAC_ADDR_SIZE); + chk_frame.dialog_token = dialog_token; + chk_frame.action_code = action_code; + chk_frame.status_code = status_code; + chk_frame.len = len; + chk_frame.max_sta_failed = max_sta_failed; + + mgmt_req.chk_frame = &chk_frame; + + mgmt_req.vdev = vdev; + mgmt_req.vdev_id = wlan_vdev_get_id(vdev); + mgmt_req.session_id = mgmt_req.vdev_id; + /* populate management req params */ + qdf_mem_copy(mgmt_req.tdls_mgmt.peer_mac.bytes, + peer_mac, QDF_MAC_ADDR_SIZE); + mgmt_req.tdls_mgmt.dialog = dialog_token; + mgmt_req.tdls_mgmt.frame_type = action_code; + mgmt_req.tdls_mgmt.len = len; + mgmt_req.tdls_mgmt.peer_capability = peer_capability; + mgmt_req.tdls_mgmt.status_code = chk_frame.status_code; + + /*populate the additional IE's */ + mgmt_req.cmd_buf = buf; + mgmt_req.len = len; + + reinit_completion(&tdls_priv->tdls_mgmt_comp); + status = ucfg_tdls_send_mgmt_frame(&mgmt_req); + if (QDF_IS_STATUS_ERROR(status)) { + cfg80211_err("ucfg_tdls_send_mgmt failed err %d", status); + status = -EIO; + tdls_priv->mgmt_tx_completion_status = false; + goto error_mgmt_req; + } + + cfg80211_debug("Wait for tdls_mgmt_comp. Timeout %u ms", + WAIT_TIME_FOR_TDLS_MGMT); + + rc = wait_for_completion_timeout( + &tdls_priv->tdls_mgmt_comp, + msecs_to_jiffies(WAIT_TIME_FOR_TDLS_MGMT)); + + if ((0 == rc) || (true != tdls_priv->mgmt_tx_completion_status)) { + cfg80211_err("%s rc %ld mgmtTxCompletionStatus %u", + !rc ? "Mgmt Tx Completion timed out" : + "Mgmt Tx Completion failed", + rc, tdls_priv->mgmt_tx_completion_status); + + tdls_priv->mgmt_tx_completion_status = false; + status = -EINVAL; + goto error_mgmt_req; + } + + cfg80211_debug("Mgmt Tx Completion status %ld TxCompletion %u", + rc, tdls_priv->mgmt_tx_completion_status); + + if (chk_frame.max_sta_failed) { + status = max_sta_failed; + goto error_mgmt_req; + } + + if (TDLS_SETUP_RESPONSE == action_code || + TDLS_SETUP_CONFIRM == action_code) { + qdf_mem_copy(set_responder.peer_mac, peer_mac, + QDF_MAC_ADDR_SIZE); + set_responder.vdev = vdev; + if (TDLS_SETUP_RESPONSE == action_code) + set_responder.responder = false; + if (TDLS_SETUP_CONFIRM == action_code) + set_responder.responder = true; + ucfg_tdls_responder(&set_responder); + } + +error_mgmt_req: + return status; +} + +int wlan_tdls_antenna_switch(struct wlan_objmgr_vdev *vdev, uint32_t mode) +{ + struct vdev_osif_priv *osif_priv; + struct osif_tdls_vdev *tdls_priv; + int ret; + unsigned long rc; + + if (!vdev) { + cfg80211_err("vdev is NULL"); + return -EAGAIN; + } + + osif_priv = wlan_vdev_get_ospriv(vdev); + tdls_priv = osif_priv->osif_tdls; + + reinit_completion(&tdls_priv->tdls_antenna_switch_comp); + ret = ucfg_tdls_antenna_switch(vdev, mode); + if (QDF_IS_STATUS_ERROR(ret)) { + cfg80211_err("ucfg_tdls_antenna_switch failed err %d", ret); + ret = -EAGAIN; + goto error; + } + + rc = wait_for_completion_timeout( + &tdls_priv->tdls_antenna_switch_comp, + msecs_to_jiffies(WAIT_TIME_FOR_TDLS_ANTENNA_SWITCH)); + if (!rc) { + cfg80211_err("timeout for tdls antenna switch %ld", rc); + ret = -EAGAIN; + goto error; + } + + ret = tdls_priv->tdls_antenna_switch_status; + cfg80211_debug("tdls antenna switch status:%d", ret); +error: + return ret; +} + +static void +wlan_cfg80211_tdls_indicate_discovery(struct tdls_osif_indication *ind) +{ + struct vdev_osif_priv *osif_vdev; + + osif_vdev = wlan_vdev_get_ospriv(ind->vdev); + + cfg80211_debug("Implicit TDLS, request Send Discovery request"); + cfg80211_tdls_oper_request(osif_vdev->wdev->netdev, + ind->peer_mac, NL80211_TDLS_DISCOVERY_REQ, + false, GFP_KERNEL); +} + +static void +wlan_cfg80211_tdls_indicate_setup(struct tdls_osif_indication *ind) +{ + struct vdev_osif_priv *osif_vdev; + + osif_vdev = wlan_vdev_get_ospriv(ind->vdev); + + cfg80211_debug("Indication to request TDLS setup"); + cfg80211_tdls_oper_request(osif_vdev->wdev->netdev, + ind->peer_mac, NL80211_TDLS_SETUP, false, + GFP_KERNEL); +} + +static void +wlan_cfg80211_tdls_indicate_teardown(struct tdls_osif_indication *ind) +{ + struct vdev_osif_priv *osif_vdev; + + osif_vdev = wlan_vdev_get_ospriv(ind->vdev); + + cfg80211_debug("Teardown reason %d", ind->reason); + cfg80211_tdls_oper_request(osif_vdev->wdev->netdev, + ind->peer_mac, NL80211_TDLS_TEARDOWN, + ind->reason, GFP_KERNEL); +} + +void wlan_cfg80211_tdls_event_callback(void *user_data, + enum tdls_event_type type, + struct tdls_osif_indication *ind) +{ + struct vdev_osif_priv *osif_vdev; + struct osif_tdls_vdev *tdls_priv; + + if (!ind || !ind->vdev) { + cfg80211_err("ind: %pK", ind); + return; + } + osif_vdev = wlan_vdev_get_ospriv(ind->vdev); + tdls_priv = osif_vdev->osif_tdls; + + switch (type) { + case TDLS_EVENT_MGMT_TX_ACK_CNF: + tdls_priv->mgmt_tx_completion_status = ind->status; + complete(&tdls_priv->tdls_mgmt_comp); + break; + case TDLS_EVENT_ADD_PEER: + tdls_priv->tdls_add_peer_status = ind->status; + complete(&tdls_priv->tdls_add_peer_comp); + break; + case TDLS_EVENT_DEL_PEER: + complete(&tdls_priv->tdls_del_peer_comp); + break; + case TDLS_EVENT_DISCOVERY_REQ: + wlan_cfg80211_tdls_indicate_discovery(ind); + break; + case TDLS_EVENT_TEARDOWN_REQ: + wlan_cfg80211_tdls_indicate_teardown(ind); + break; + case TDLS_EVENT_SETUP_REQ: + wlan_cfg80211_tdls_indicate_setup(ind); + break; + case TDLS_EVENT_TEARDOWN_LINKS_DONE: + complete(&tdls_priv->tdls_teardown_comp); + break; + case TDLS_EVENT_USER_CMD: + tdls_priv->tdls_user_cmd_len = ind->status; + complete(&tdls_priv->tdls_user_cmd_comp); + break; + + case TDLS_EVENT_ANTENNA_SWITCH: + tdls_priv->tdls_antenna_switch_status = ind->status; + complete(&tdls_priv->tdls_antenna_switch_comp); + default: + break; + } +}