diff --git a/core/hdd/inc/wlan_hdd_cfr.h b/core/hdd/inc/wlan_hdd_cfr.h new file mode 100644 index 0000000000000..ed3b206e7b59a --- /dev/null +++ b/core/hdd/inc/wlan_hdd_cfr.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2020, 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_hdd_cfr.h + * + * WLAN Host Device Driver cfr capture implementation + * + */ + +#if !defined(_WLAN_HDD_CFR_H) +#define _WLAN_HDD_CFR_H + +#ifdef WLAN_CFR_ENABLE + +#define HDD_INVALID_GROUP_ID 16 +#define ENHANCED_CFR_VERSION 2 + +/** + * wlan_hdd_cfg80211_peer_cfr_capture_cfg() - configure peer cfr capture + * @wiphy: WIPHY structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of the data received + * + * This function starts CFR capture + * + * Return: 0 on success and errno on failure + */ +int +wlan_hdd_cfg80211_peer_cfr_capture_cfg(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len); + +extern const struct nla_policy cfr_config_policy[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX + 1]; + +#define FEATURE_CFR_VENDOR_COMMANDS \ +{ \ + .info.vendor_id = QCA_NL80211_VENDOR_ID, \ + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG, \ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | \ + WIPHY_VENDOR_CMD_NEED_NETDEV, \ + .doit = wlan_hdd_cfg80211_peer_cfr_capture_cfg, \ + vendor_command_policy(cfr_config_policy, \ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX) \ +}, +#else +#define FEATURE_CFR_VENDOR_COMMANDS +#endif /* WLAN_CFR_ENABLE */ +#endif /* _WLAN_HDD_CFR_H */ + diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c index dc843e4527ee1..12c82b837a0d6 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.c +++ b/core/hdd/src/wlan_hdd_cfg80211.c @@ -149,6 +149,7 @@ #include "wlan_hdd_btc_chain_mode.h" #include "os_if_nan.h" #include "wlan_hdd_apf.h" +#include "wlan_hdd_cfr.h" #define g_mode_rates_size (12) #define a_mode_rates_size (8) @@ -14301,6 +14302,7 @@ const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = { FEATURE_BSS_TRANSITION_VENDOR_COMMANDS FEATURE_SPECTRAL_SCAN_VENDOR_COMMANDS + FEATURE_CFR_VENDOR_COMMANDS FEATURE_11AX_VENDOR_COMMANDS { diff --git a/core/hdd/src/wlan_hdd_cfr.c b/core/hdd/src/wlan_hdd_cfr.c new file mode 100644 index 0000000000000..9170dc76a2d13 --- /dev/null +++ b/core/hdd/src/wlan_hdd_cfr.c @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2020, 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_hdd_cfr.c + * + * WLAN Host Device Driver CFR capture Implementation + */ + +#include +#include +#include +#include +#include "wlan_hdd_includes.h" +#include "osif_sync.h" +#include "wlan_hdd_cfr.h" +#include "wlan_cfr_ucfg_api.h" + +const struct nla_policy cfr_config_policy[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_CFR_PEER_MAC_ADDR] = { + .type = NLA_BINARY, .len = QDF_MAC_ADDR_SIZE}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE] = {.type = NLA_FLAG}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_BANDWIDTH] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_PERIODICITY] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_VERSION] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_PERIODIC_CFR_CAPTURE_ENABLE] = { + .type = NLA_FLAG}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE_GROUP_BITMAP] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_DURATION] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_INTERVAL] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_TYPE] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_UL_MU_MASK] = {.type = NLA_U64}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_FREEZE_TLV_DELAY_COUNT] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NUMBER] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA] = { + .type = NLA_BINARY, .len = QDF_MAC_ADDR_SIZE}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA] = { + .type = NLA_BINARY, .len = QDF_MAC_ADDR_SIZE}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK] = { + .type = NLA_BINARY, .len = QDF_MAC_ADDR_SIZE}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA_MASK] = { + .type = NLA_BINARY, .len = QDF_MAC_ADDR_SIZE}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NSS] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_BW] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_MGMT_FILTER] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_CTRL_FILTER] = { + .type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_DATA_FILTER] = { + .type = NLA_U32}, +}; + +static QDF_STATUS +wlan_cfg80211_cfr_set_group_config(struct wlan_objmgr_vdev *vdev, + struct nlattr *tb[]) +{ + struct cfr_wlanconfig_param params = { 0 }; + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NUMBER]) { + params.grp_id = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NUMBER]); + hdd_debug("group_id %d", params.grp_id); + } + + if (params.grp_id >= HDD_INVALID_GROUP_ID) { + hdd_err("invalid group id"); + return QDF_STATUS_E_INVAL; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA]) { + nla_memcpy(¶ms.ta[0], + tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA], + QDF_MAC_ADDR_SIZE); + hdd_debug("ta " QDF_MAC_ADDR_STR, + QDF_MAC_ADDR_ARRAY(¶ms.ta[0])); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK]) { + nla_memcpy(¶ms.ta_mask[0], + tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK], + QDF_MAC_ADDR_SIZE); + hdd_debug("ta_mask " QDF_MAC_ADDR_STR, + QDF_MAC_ADDR_ARRAY(¶ms.ta_mask[0])); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA]) { + nla_memcpy(¶ms.ra[0], + tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA], + QDF_MAC_ADDR_SIZE); + hdd_debug("ra " QDF_MAC_ADDR_STR, + QDF_MAC_ADDR_ARRAY(¶ms.ra[0])); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA_MASK]) { + nla_memcpy(¶ms.ra_mask[0], + tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA_MASK], + QDF_MAC_ADDR_SIZE); + hdd_debug("ra_mask " QDF_MAC_ADDR_STR, + QDF_MAC_ADDR_ARRAY(¶ms.ra_mask[0])); + } + + if (!qdf_is_macaddr_zero((struct qdf_mac_addr *)¶ms.ta) || + !qdf_is_macaddr_zero((struct qdf_mac_addr *)¶ms.ra) || + !qdf_is_macaddr_zero((struct qdf_mac_addr *)¶ms.ta_mask) || + !qdf_is_macaddr_zero((struct qdf_mac_addr *)¶ms.ra_mask)) { + hdd_debug("set tara config"); + ucfg_cfr_set_tara_config(vdev, ¶ms); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NSS]) { + params.nss = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NSS]); + hdd_debug("nss %d", params.nss); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_BW]) { + params.bw = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_BW]); + hdd_debug("bw %d", params.bw); + } + + if (params.nss || params.bw) { + hdd_debug("set bw nss"); + ucfg_cfr_set_bw_nss(vdev, ¶ms); + } + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_MGMT_FILTER]) { + params.expected_mgmt_subtype = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_MGMT_FILTER]); + hdd_debug("expected_mgmt_subtype %d(%x)", + params.expected_mgmt_subtype, + params.expected_mgmt_subtype); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_CTRL_FILTER]) { + params.expected_ctrl_subtype = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_CTRL_FILTER]); + hdd_debug("expected_mgmt_subtype %d(%x)", + params.expected_ctrl_subtype, + params.expected_ctrl_subtype); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_DATA_FILTER]) { + params.expected_data_subtype = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_DATA_FILTER]); + hdd_debug("expected_mgmt_subtype %d(%x)", + params.expected_data_subtype, + params.expected_data_subtype); + } + if (!params.expected_mgmt_subtype || + !params.expected_ctrl_subtype || + !params.expected_data_subtype) { + hdd_debug("set frame type"); + ucfg_cfr_set_frame_type_subtype(vdev, ¶ms); + } + + return QDF_STATUS_SUCCESS; +} + +static int +wlan_cfg80211_cfr_set_config(struct wlan_objmgr_vdev *vdev, + struct nlattr *tb[]) +{ + struct nlattr *group[QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX + 1]; + struct nlattr *group_list; + struct cfr_wlanconfig_param params = { 0 }; + enum capture_type type; + int rem = 0; + int maxtype; + int attr; + uint64_t ul_mu_user_mask = 0; + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_DURATION]) { + params.cap_dur = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_DURATION]); + ucfg_cfr_set_capture_duration(vdev, ¶ms); + hdd_debug("params.cap_dur %d", params.cap_dur); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_INTERVAL]) { + params.cap_intvl = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_INTERVAL]); + ucfg_cfr_set_capture_interval(vdev, ¶ms); + hdd_debug("params.cap_intvl %d", params.cap_intvl); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_TYPE]) { + type = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_TYPE]); + ucfg_cfr_set_rcc_mode(vdev, type, 1); + hdd_debug("type %d", type); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_UL_MU_MASK]) { + ul_mu_user_mask = nla_get_u64(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_UL_MU_MASK]); + hdd_debug("ul_mu_user_mask_lower %d", + params.ul_mu_user_mask_lower); + } + + if (ul_mu_user_mask) { + params.ul_mu_user_mask_lower = + (uint32_t)(ul_mu_user_mask & 0xffffffff); + params.ul_mu_user_mask_lower = + (uint32_t)(ul_mu_user_mask >> 32); + hdd_debug("set ul mu user maks"); + ucfg_cfr_set_ul_mu_user_mask(vdev, ¶ms); + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_FREEZE_TLV_DELAY_COUNT]) { + params.freeze_tlv_delay_cnt_thr = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_FREEZE_TLV_DELAY_COUNT]); + if (params.freeze_tlv_delay_cnt_thr) { + params.freeze_tlv_delay_cnt_en = 1; + ucfg_cfr_set_freeze_tlv_delay_cnt(vdev, ¶ms); + hdd_debug("freeze_tlv_delay_cnt_thr %d", + params.freeze_tlv_delay_cnt_thr); + } + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TABLE]) { + maxtype = QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX; + attr = QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TABLE; + nla_for_each_nested(group_list, tb[attr], rem) { + if (wlan_cfg80211_nla_parse(group, maxtype, + nla_data(group_list), + nla_len(group_list), + cfr_config_policy)) { + hdd_err("nla_parse failed for cfr config group"); + return -EINVAL; + } + wlan_cfg80211_cfr_set_group_config(vdev, group); + } + } + + return 0; +} + +static int +wlan_cfg80211_peer_cfr_capture_cfg(struct wiphy *wiphy, + struct hdd_adapter *adapter, + const void *data, + int data_len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX + 1]; + struct cfr_wlanconfig_param params = { 0 }; + struct wlan_objmgr_vdev *vdev; + uint8_t version = 0; + bool is_start_capture = false; + QDF_STATUS status; + int ret; + + if (wlan_cfg80211_nla_parse( + tb, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX, + data, + data_len, + cfr_config_policy)) { + hdd_err("Invalid ATTR"); + return -EINVAL; + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_VERSION]) { + version = nla_get_u8(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_VERSION]); + hdd_debug("version %d", version); + if (version != ENHANCED_CFR_VERSION) { + hdd_err("unsupported version"); + return -EFAULT; + } + } + + if (tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE]) { + is_start_capture = nla_get_flag(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE]); + } + + if (is_start_capture && + !tb[QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE_GROUP_BITMAP]) { + hdd_err("Invalid group bitmap"); + return -EINVAL; + } + + vdev = adapter->vdev; + status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_CFR_ID); + if (QDF_IS_STATUS_ERROR(status)) { + hdd_err("can't get vdev"); + return qdf_status_to_os_return(status); + } + + if (is_start_capture) { + ret = wlan_cfg80211_cfr_set_config(vdev, tb); + if (ret) { + hdd_err("set config failed"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_CFR_ID); + return ret; + } + params.en_cfg = nla_get_u32(tb[ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE_GROUP_BITMAP]); + hdd_debug("params.en_cfg %d", params.en_cfg); + ucfg_cfr_set_en_bitmap(vdev, ¶ms); + } else { + hdd_debug("cleanup rcc mode"); + ucfg_cfr_set_rcc_mode(vdev, RCC_DIS_ALL_MODE, 0); + } + ucfg_cfr_subscribe_ppdu_desc(wlan_vdev_get_pdev(vdev), + is_start_capture); + ucfg_cfr_committed_rcc_config(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_CFR_ID); + + return 0; +} + +static int __wlan_hdd_cfg80211_peer_cfr_capture_cfg(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + int ret; + struct hdd_context *hdd_ctx = wiphy_priv(wiphy); + struct net_device *dev = wdev->netdev; + struct hdd_adapter *adapter; + + hdd_enter(); + + ret = wlan_hdd_validate_context(hdd_ctx); + if (ret) + return ret; + + if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) { + hdd_err("Command not allowed in FTM mode"); + return -EPERM; + } + + adapter = WLAN_HDD_GET_PRIV_PTR(dev); + if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) + return -EINVAL; + + wlan_cfg80211_peer_cfr_capture_cfg(wiphy, adapter, + data, data_len); + + hdd_exit(); + + return ret; +} + +int wlan_hdd_cfg80211_peer_cfr_capture_cfg(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct osif_psoc_sync *psoc_sync; + int errno; + + errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync); + if (errno) + return errno; + + errno = __wlan_hdd_cfg80211_peer_cfr_capture_cfg(wiphy, wdev, + data, data_len); + + osif_psoc_sync_op_stop(psoc_sync); + + return errno; +}