From 5899ce5022bebd903a27ff6bab120f2f9437bcd4 Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Mon, 2 Dec 2019 16:02:05 +0200 Subject: [PATCH 1/4] wil6210: use country specific board file upon reg domain change wil6210 device needs to use country specific board file while in China regulatory domain. Register cfg80211 reg_notifier and switch board file if needed according to new regulatory domain. This feature is disabled by default and can be enabled with a new country_specific_board_file module parameter. Change-Id: I2f4555d2cf6a565c2c17a70a7a6307cc5ebbc27b Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez [liord@codeaurora.org: fix merge conflicts] Signed-off-by: Lior David Signed-off-by: Alexei Avshalom Lazar --- drivers/net/wireless/ath/wil6210/cfg80211.c | 66 +++++++++++++++++++++ drivers/net/wireless/ath/wil6210/main.c | 28 +++++++-- drivers/net/wireless/ath/wil6210/wil6210.h | 4 ++ 3 files changed, 92 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 6fcae5ce1b77..f735ebf1c0d1 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -14,6 +14,7 @@ #include "fw.h" #define WIL_MAX_ROC_DURATION_MS 5000 +#define CTRY_CHINA "CN" #define WIL_EDMG_CHANNEL_9_SUBCHANNELS (BIT(0) | BIT(1)) #define WIL_EDMG_CHANNEL_10_SUBCHANNELS (BIT(1) | BIT(2)) @@ -41,6 +42,10 @@ static struct wiphy_wowlan_support wil_wowlan_support = { }; #endif +static bool country_specific_board_file; +module_param(country_specific_board_file, bool, 0444); +MODULE_PARM_DESC(country_specific_board_file, " switch board file upon regulatory domain change (Default: false)"); + static bool ignore_reg_hints = true; module_param(ignore_reg_hints, bool, 0444); MODULE_PARM_DESC(ignore_reg_hints, " Ignore OTA regulatory hints (Default: true)"); @@ -2604,6 +2609,65 @@ wil_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev, return 0; } +static int wil_switch_board_file(struct wil6210_priv *wil, + const u8 *new_regdomain) +{ + int rc = 0; + + if (!country_specific_board_file) + return 0; + + if (memcmp(wil->regdomain, CTRY_CHINA, 2) == 0) { + wil_info(wil, "moving out of China reg domain, use default board file\n"); + wil->board_file_country[0] = '\0'; + } else if (memcmp(new_regdomain, CTRY_CHINA, 2) == 0) { + wil_info(wil, "moving into China reg domain, use country specific board file\n"); + strlcpy(wil->board_file_country, CTRY_CHINA, + sizeof(wil->board_file_country)); + } else { + return 0; + } + + /* need to switch board file - reset the device */ + + mutex_lock(&wil->mutex); + + if (!wil_has_active_ifaces(wil, true, false) || + wil_is_recovery_blocked(wil)) + /* new board file will be used in next FW load */ + goto out; + + __wil_down(wil); + rc = __wil_up(wil); + +out: + mutex_unlock(&wil->mutex); + return rc; +} + +static void wil_cfg80211_reg_notify(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + int rc; + + wil_info(wil, "cfg reg_notify %c%c%s%s initiator %d hint_type %d\n", + request->alpha2[0], request->alpha2[1], + request->intersect ? " intersect" : "", + request->processed ? " processed" : "", + request->initiator, request->user_reg_hint_type); + + if (memcmp(wil->regdomain, request->alpha2, 2) == 0) + /* reg domain did not change */ + return; + + rc = wil_switch_board_file(wil, request->alpha2); + if (rc) + wil_err(wil, "switch board file failed %d\n", rc); + + memcpy(wil->regdomain, request->alpha2, 2); +} + static int wil_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_update_ft_ies_params *ftie) @@ -2724,6 +2788,8 @@ static void wil_wiphy_init(struct wiphy *wiphy) wiphy->mgmt_stypes = wil_mgmt_stypes; wiphy->features |= NL80211_FEATURE_SK_TX_STATUS; + wiphy->reg_notifier = wil_cfg80211_reg_notify; + wiphy->n_vendor_commands = ARRAY_SIZE(wil_nl80211_vendor_commands); wiphy->vendor_commands = wil_nl80211_vendor_commands; wiphy->vendor_events = wil_nl80211_vendor_events; diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 89238bd046e9..a2243027cd24 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -1291,6 +1291,8 @@ void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) void wil_get_board_file(struct wil6210_priv *wil, char *buf, size_t len) { const char *board_file; + const char *ext; + int prefix_len; const char *wil_talyn_fw_name = ftm_mode ? WIL_FW_NAME_FTM_TALYN : WIL_FW_NAME_TALYN; @@ -1306,7 +1308,21 @@ void wil_get_board_file(struct wil6210_priv *wil, char *buf, size_t len) board_file = WIL_BOARD_FILE_NAME; } - strlcpy(buf, board_file, len); + if (wil->board_file_country[0] == '\0') { + strlcpy(buf, board_file, len); + return; + } + + /* use country specific board file */ + if (len < strlen(board_file) + 4 /* for _XX and terminating null */) + return; + + ext = strrchr(board_file, '.'); + prefix_len = (ext ? ext - board_file : strlen(board_file)); + snprintf(buf, len, "%.*s_%.2s", + prefix_len, board_file, wil->board_file_country); + if (ext) + strlcat(buf, ext, len); } static int wil_get_bl_info(struct wil6210_priv *wil) @@ -1707,16 +1723,16 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) if (load_fw) { char board_file[WIL_BOARD_FILE_MAX_NAMELEN]; - if (wil->secured_boot) { - wil_err(wil, "secured boot is not supported\n"); - return -ENOTSUPP; - } - board_file[0] = '\0'; wil_get_board_file(wil, board_file, sizeof(board_file)); wil_info(wil, "Use firmware <%s> + board <%s>\n", wil->wil_fw_name, board_file); + if (wil->secured_boot) { + wil_err(wil, "secured boot is not supported\n"); + return -ENOTSUPP; + } + if (!no_flash) wil_bl_prepare_halt(wil); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index fddc8bccc524..aef6e90b0fef 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -934,6 +934,7 @@ struct wil6210_priv { const char *hw_name; const char *wil_fw_name; char *board_file; + char board_file_country[3]; /* alpha2 */ u32 num_of_brd_entries; struct wil_brd_info *brd_info; DECLARE_BITMAP(hw_capa, hw_capa_last); @@ -1045,6 +1046,9 @@ struct wil6210_priv { short direct; } snr_thresh; + /* current reg domain configured in kernel */ + char regdomain[3]; /* alpha2 */ + struct notifier_block pm_notify; bool suspend_resp_rcvd; From efb3116f2ae80eb87d23c02308863d326a50c573 Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Mon, 2 Dec 2019 16:04:02 +0200 Subject: [PATCH 2/4] wil6210: support configurable board file via sysfs Add a sysfs allowing to override the board file containing the RF characteristics of the 11ad device. The new board file will be used the next time the interface is brought up. This change is intended to support platforms with multiple 11ad devices connected. Each 11ad device can be connected to a different antenna module and the HW cannot automatically detect the type of antenna connected, so the board file must be explicitly specified for each device. Change-Id: I40746032d9314057c64575bca96f3445816e5457 Signed-off-by: Dedy Lansky Signed-off-by: Lior David Signed-off-by: Maya Erez [liord@codeaurora.org: merge conflicts, checkpatch errors] Signed-off-by: Lior David Signed-off-by: Alexei Avshalom Lazar --- drivers/net/wireless/ath/wil6210/main.c | 1 + drivers/net/wireless/ath/wil6210/sysfs.c | 45 ++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index a2243027cd24..c0c4f913115d 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -832,6 +832,7 @@ void wil_priv_deinit(struct wil6210_priv *wil) wmi_event_flush(wil); destroy_workqueue(wil->wq_service); destroy_workqueue(wil->wmi_wq); + kfree(wil->board_file); kfree(wil->brd_info); } diff --git a/drivers/net/wireless/ath/wil6210/sysfs.c b/drivers/net/wireless/ath/wil6210/sysfs.c index 501ee1f1bf5e..f872e8358033 100644 --- a/drivers/net/wireless/ath/wil6210/sysfs.c +++ b/drivers/net/wireless/ath/wil6210/sysfs.c @@ -81,6 +81,50 @@ ftm_txrx_offset_store(struct device *dev, static DEVICE_ATTR_RW(ftm_txrx_offset); +static ssize_t +board_file_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct wil6210_priv *wil = dev_get_drvdata(dev); + + wil_get_board_file(wil, buf, PAGE_SIZE); + strlcat(buf, "\n", PAGE_SIZE); + return strlen(buf); +} + +static ssize_t +board_file_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wil6210_priv *wil = dev_get_drvdata(dev); + size_t len; + + mutex_lock(&wil->mutex); + + kfree(wil->board_file); + wil->board_file = NULL; + + len = count; + if (buf[count - 1] == '\n') + len--; + len = strnlen(buf, len); + if (len > 0) { + wil->board_file = kmalloc(len + 1, GFP_KERNEL); + if (!wil->board_file) { + mutex_unlock(&wil->mutex); + return -ENOMEM; + } + strlcpy(wil->board_file, buf, len + 1); + } + mutex_unlock(&wil->mutex); + + return count; +} + +static DEVICE_ATTR_RW(board_file); + static ssize_t thermal_throttling_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -291,6 +335,7 @@ static DEVICE_ATTR_RW(snr_thresh); static struct attribute *wil6210_sysfs_entries[] = { &dev_attr_ftm_txrx_offset.attr, + &dev_attr_board_file.attr, &dev_attr_thermal_throttling.attr, &dev_attr_fst_link_loss.attr, &dev_attr_snr_thresh.attr, From 2347d58a7c8440975ba174ce7d159b1c16803425 Mon Sep 17 00:00:00 2001 From: Alexei Avshalom Lazar Date: Mon, 2 Dec 2019 16:10:28 +0200 Subject: [PATCH 3/4] wil6210: BRP antenna limit API Added vendor command for setting BRP antenna limit. It allows user space to set the number of antennas that will be active in different modes for each connection. Change-Id: I8a0eb1fa4c74b169faeec2761818a5bac60a8196 Signed-off-by: Alexei Avshalom Lazar Signed-off-by: Lior David --- drivers/net/wireless/ath/wil6210/cfg80211.c | 142 ++++++++++++++++++-- 1 file changed, 131 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index f735ebf1c0d1..1819b2a35937 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -59,6 +59,9 @@ MODULE_PARM_DESC(ignore_reg_hints, " Ignore OTA regulatory hints (Default: true) .max_power = 40, \ } +#define WIL_BRP_ANT_LIMIT_MIN (1) +#define WIL_BRP_ANT_LIMIT_MAX (27) + static struct ieee80211_channel wil_60ghz_channels[] = { CHAN60G(1, 0), CHAN60G(2, 0), @@ -152,7 +155,7 @@ void update_supported_bands(struct wil6210_priv *wil) #define WIL_MAX_RF_SECTORS (128) #define WIL_CID_ALL (0xff) -enum qca_wlan_vendor_attr_rf_sector { +enum qca_wlan_vendor_attr_wil { QCA_ATTR_MAC_ADDR = 6, QCA_ATTR_PAD = 13, QCA_ATTR_TSF = 29, @@ -160,7 +163,9 @@ enum qca_wlan_vendor_attr_rf_sector { QCA_ATTR_DMG_RF_SECTOR_TYPE = 31, QCA_ATTR_DMG_RF_MODULE_MASK = 32, QCA_ATTR_DMG_RF_SECTOR_CFG = 33, - QCA_ATTR_DMG_RF_SECTOR_MAX, + QCA_ATTR_BRP_ANT_LIMIT_MODE = 38, + QCA_ATTR_BRP_ANT_NUM_LIMIT = 39, + QCA_ATTR_WIL_MAX, }; enum qca_wlan_vendor_attr_dmg_rf_sector_type { @@ -185,8 +190,22 @@ enum qca_wlan_vendor_attr_dmg_rf_sector_cfg { QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1 }; +enum qca_wlan_vendor_attr_brp_ant_limit_mode { + QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE, + QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_EFFECTIVE, + QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_FORCE, + QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODES_NUM +}; + static const struct -nla_policy wil_rf_sector_policy[QCA_ATTR_DMG_RF_SECTOR_MAX + 1] = { +nla_policy wil_brp_ant_limit_policy[QCA_ATTR_WIL_MAX + 1] = { + [QCA_ATTR_MAC_ADDR] = { .len = ETH_ALEN }, + [QCA_ATTR_BRP_ANT_NUM_LIMIT] = { .type = NLA_U8 }, + [QCA_ATTR_BRP_ANT_LIMIT_MODE] = { .type = NLA_U8 }, +}; + +static const struct +nla_policy wil_rf_sector_policy[QCA_ATTR_WIL_MAX + 1] = { [QCA_ATTR_MAC_ADDR] = { .len = ETH_ALEN }, [QCA_ATTR_DMG_RF_SECTOR_INDEX] = { .type = NLA_U16 }, [QCA_ATTR_DMG_RF_SECTOR_TYPE] = { .type = NLA_U8 }, @@ -219,6 +238,7 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG = 140, QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR = 141, QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR = 142, + QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT = 153, }; static int wil_rf_sector_get_cfg(struct wiphy *wiphy, @@ -233,6 +253,8 @@ static int wil_rf_sector_get_selected(struct wiphy *wiphy, static int wil_rf_sector_set_selected(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len); +static int wil_brp_set_ant_limit(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int data_len); /* vendor specific commands */ static const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = { @@ -312,6 +334,13 @@ static const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = { .policy = wil_rf_sector_policy, .doit = wil_rf_sector_set_selected }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wil_brp_set_ant_limit + }, }; /* vendor specific events */ @@ -2960,7 +2989,7 @@ static int wil_rf_sector_get_cfg(struct wiphy *wiphy, struct wil6210_priv *wil = wdev_to_wil(wdev); struct wil6210_vif *vif = wdev_to_vif(wil, wdev); int rc; - struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; + struct nlattr *tb[QCA_ATTR_WIL_MAX + 1]; u16 sector_index; u8 sector_type; u32 rf_modules_vec; @@ -2979,7 +3008,7 @@ static int wil_rf_sector_get_cfg(struct wiphy *wiphy, if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) return -EOPNOTSUPP; - rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, + rc = nla_parse_deprecated(tb, QCA_ATTR_WIL_MAX, data, data_len, wil_rf_sector_policy, NULL); if (rc) { wil_err(wil, "Invalid rf sector ATTR\n"); @@ -3081,7 +3110,7 @@ static int wil_rf_sector_set_cfg(struct wiphy *wiphy, struct wil6210_priv *wil = wdev_to_wil(wdev); struct wil6210_vif *vif = wdev_to_vif(wil, wdev); int rc, tmp; - struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; + struct nlattr *tb[QCA_ATTR_WIL_MAX + 1]; struct nlattr *tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1]; u16 sector_index, rf_module_index; u8 sector_type; @@ -3099,7 +3128,7 @@ static int wil_rf_sector_set_cfg(struct wiphy *wiphy, if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) return -EOPNOTSUPP; - rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, + rc = nla_parse_deprecated(tb, QCA_ATTR_WIL_MAX, data, data_len, wil_rf_sector_policy, NULL); if (rc) { wil_err(wil, "Invalid rf sector ATTR\n"); @@ -3193,7 +3222,7 @@ static int wil_rf_sector_get_selected(struct wiphy *wiphy, struct wil6210_priv *wil = wdev_to_wil(wdev); struct wil6210_vif *vif = wdev_to_vif(wil, wdev); int rc; - struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; + struct nlattr *tb[QCA_ATTR_WIL_MAX + 1]; u8 sector_type, mac_addr[ETH_ALEN]; int cid = 0; struct wmi_get_selected_rf_sector_index_cmd cmd; @@ -3208,7 +3237,7 @@ static int wil_rf_sector_get_selected(struct wiphy *wiphy, if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) return -EOPNOTSUPP; - rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, + rc = nla_parse_deprecated(tb, QCA_ATTR_WIL_MAX, data, data_len, wil_rf_sector_policy, NULL); if (rc) { wil_err(wil, "Invalid rf sector ATTR\n"); @@ -3308,7 +3337,7 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy, struct wil6210_priv *wil = wdev_to_wil(wdev); struct wil6210_vif *vif = wdev_to_vif(wil, wdev); int rc; - struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; + struct nlattr *tb[QCA_ATTR_WIL_MAX + 1]; u16 sector_index; u8 sector_type, mac_addr[ETH_ALEN], i; int cid = 0; @@ -3316,7 +3345,7 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy, if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) return -EOPNOTSUPP; - rc = nla_parse_deprecated(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, + rc = nla_parse_deprecated(tb, QCA_ATTR_WIL_MAX, data, data_len, wil_rf_sector_policy, NULL); if (rc) { wil_err(wil, "Invalid rf sector ATTR\n"); @@ -3398,3 +3427,94 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy, return rc; } + +static int +wil_brp_wmi_set_ant_limit(struct wil6210_priv *wil, u8 mid, u8 cid, + u8 limit_mode, u8 antenna_num_limit) +{ + int rc; + struct wmi_brp_set_ant_limit_cmd cmd = { + .cid = cid, + .limit_mode = limit_mode, + .ant_limit = antenna_num_limit, + }; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_brp_set_ant_limit_event evt; + } __packed reply; + + reply.evt.status = WMI_FW_STATUS_FAILURE; + + rc = wmi_call(wil, WMI_BRP_SET_ANT_LIMIT_CMDID, mid, &cmd, sizeof(cmd), + WMI_BRP_SET_ANT_LIMIT_EVENTID, &reply, + sizeof(reply), 250); + if (rc) + return rc; + + if (reply.evt.status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "brp set antenna limit failed with status %d\n", + reply.evt.status); + rc = -EINVAL; + } + + return rc; +} + +static int wil_brp_set_ant_limit(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct wil6210_priv *wil = wdev_to_wil(wdev); + struct wil6210_vif *vif = wdev_to_vif(wil, wdev); + struct nlattr *tb[QCA_ATTR_WIL_MAX + 1]; + u8 mac_addr[ETH_ALEN]; + u8 antenna_num_limit = 0; + u8 limit_mode; + int cid = 0; + int rc; + + if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) + return -ENOTSUPP; + + rc = nla_parse(tb, QCA_ATTR_WIL_MAX, data, data_len, + wil_brp_ant_limit_policy, NULL); + if (rc) { + wil_err(wil, "Invalid ant limit ATTR\n"); + return rc; + } + + if (!tb[QCA_ATTR_BRP_ANT_LIMIT_MODE] || !tb[QCA_ATTR_MAC_ADDR]) { + wil_err(wil, "Invalid antenna limit spec\n"); + return -EINVAL; + } + + limit_mode = nla_get_u8(tb[QCA_ATTR_BRP_ANT_LIMIT_MODE]); + if (limit_mode >= QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODES_NUM) { + wil_err(wil, "Invalid limit mode %d\n", limit_mode); + return -EINVAL; + } + + if (limit_mode != QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE) { + if (!tb[QCA_ATTR_BRP_ANT_NUM_LIMIT]) { + wil_err(wil, "Invalid limit number\n"); + return -EINVAL; + } + + antenna_num_limit = nla_get_u8(tb[QCA_ATTR_BRP_ANT_NUM_LIMIT]); + if (antenna_num_limit > WIL_BRP_ANT_LIMIT_MAX || + antenna_num_limit < WIL_BRP_ANT_LIMIT_MIN) { + wil_err(wil, "Invalid number of antenna limit: %d\n", + antenna_num_limit); + return -EINVAL; + } + } + + ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR])); + cid = wil_find_cid(wil, vif->mid, mac_addr); + if (cid < 0) { + wil_err(wil, "invalid MAC address %pM\n", mac_addr); + return -ENOENT; + } + + return wil_brp_wmi_set_ant_limit(wil, vif->mid, cid, limit_mode, + antenna_num_limit); +} From 283c1dcd17ed571119aa452c4c170bff34fe87af Mon Sep 17 00:00:00 2001 From: Alexei Avshalom Lazar Date: Wed, 4 Dec 2019 09:52:08 +0200 Subject: [PATCH 4/4] wil6210: publish/receive WMI events/commands through nl For general debug purposes added functionality for sending WMI events and receiving WMI commands to/from user-space through vendor commands. Change-Id: I889d30bf1f060f75d7ae933735d1d959e53e513e Signed-off-by: Alexei Avshalom Lazar [liord@codeaurora.org: fix merge conflicts, checkpatch errors] Signed-off-by: Lior David --- drivers/net/wireless/ath/wil6210/cfg80211.c | 235 ++++++++++++++++++++ drivers/net/wireless/ath/wil6210/ftm.h | 1 + drivers/net/wireless/ath/wil6210/main.c | 2 + drivers/net/wireless/ath/wil6210/wil6210.h | 6 + drivers/net/wireless/ath/wil6210/wmi.c | 23 +- 5 files changed, 262 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 1819b2a35937..05b25f43f2b5 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -102,6 +102,49 @@ static int wil_tx_cb_mode_to_n_bonded(u8 cb_mode) } } +enum wil_nl_60g_cmd_type { + NL_60G_CMD_FW_WMI, + NL_60G_CMD_DEBUG, + NL_60G_CMD_STATISTICS, + NL_60G_CMD_REGISTER, +}; + +enum wil_nl_60g_evt_type { + NL_60G_EVT_DRIVER_ERROR, + NL_60G_EVT_FW_ERROR, + NL_60G_EVT_FW_WMI, + NL_60G_EVT_DRIVER_SHUTOWN, + NL_60G_EVT_DRIVER_DEBUG_EVENT, +}; + +enum wil_nl_60g_debug_cmd { + NL_60G_DBG_FORCE_WMI_SEND, +}; + +struct wil_nl_60g_send_receive_wmi { + u32 cmd_id; /* enum wmi_command_id or enum wmi_event_id */ + u8 reserved[2]; + u8 dev_id; /* mid */ + u16 buf_len; + u8 buf[0]; +} __packed; + +struct wil_nl_60g_event { + u32 evt_type; /* wil_nl_60g_evt_type */ + u32 buf_len; + u8 reserved[9]; + u8 buf[0]; +} __packed; + +struct wil_nl_60g_debug { /* NL_60G_CMD_DEBUG */ + u32 cmd_id; /* wil_nl_60g_debug_cmd */ +} __packed; + +struct wil_nl_60g_debug_force_wmi { + struct wil_nl_60g_debug hdr; + u32 enable; +} __packed; + static void wil_memdup_ie(u8 **pdst, size_t *pdst_len, const u8 *src, size_t src_len) { @@ -157,6 +200,8 @@ void update_supported_bands(struct wil6210_priv *wil) enum qca_wlan_vendor_attr_wil { QCA_ATTR_MAC_ADDR = 6, + QCA_ATTR_FEATURE_FLAGS = 7, + QCA_ATTR_TEST = 8, QCA_ATTR_PAD = 13, QCA_ATTR_TSF = 29, QCA_ATTR_DMG_RF_SECTOR_INDEX = 30, @@ -168,6 +213,9 @@ enum qca_wlan_vendor_attr_wil { QCA_ATTR_WIL_MAX, }; +#define WIL_ATTR_60G_CMD_TYPE QCA_ATTR_FEATURE_FLAGS +#define WIL_ATTR_60G_BUF QCA_ATTR_TEST + enum qca_wlan_vendor_attr_dmg_rf_sector_type { QCA_ATTR_DMG_RF_SECTOR_TYPE_RX, QCA_ATTR_DMG_RF_SECTOR_TYPE_TX, @@ -224,7 +272,14 @@ nla_policy wil_rf_sector_cfg_policy[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1] = { [QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16] = { .type = NLA_U32 }, }; +static const struct +nla_policy wil_nl_60g_policy[QCA_ATTR_WIL_MAX + 1] = { + [WIL_ATTR_60G_CMD_TYPE] = { .type = NLA_U32 }, + [WIL_ATTR_60G_BUF] = { .type = NLA_BINARY }, +}; + enum qca_nl80211_vendor_subcmds { + QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA = 128, QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION = 129, QCA_NL80211_VENDOR_SUBCMD_FTM_ABORT_SESSION = 130, @@ -256,6 +311,8 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy, static int wil_brp_set_ant_limit(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len); +static int wil_nl_60g_handle_cmd(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int data_len); /* vendor specific commands */ static const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = { { @@ -341,6 +398,13 @@ static const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = { WIPHY_VENDOR_CMD_NEED_RUNNING, .doit = wil_brp_set_ant_limit }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_UNSPEC, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = wil_nl_60g_handle_cmd + }, }; /* vendor specific events */ @@ -357,6 +421,10 @@ static const struct nl80211_vendor_cmd_info wil_nl80211_vendor_events[] = { .vendor_id = QCA_NL80211_VENDOR_ID, .subcmd = QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT }, + [QCA_EVENT_UNSPEC_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_UNSPEC + }, }; static struct ieee80211_supported_band wil_band_60ghz = { @@ -3518,3 +3586,170 @@ static int wil_brp_set_ant_limit(struct wiphy *wiphy, struct wireless_dev *wdev, return wil_brp_wmi_set_ant_limit(wil, vif->mid, cid, limit_mode, antenna_num_limit); } + +static int wil_nl_60g_handle_cmd(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct wil6210_priv *wil = wdev_to_wil(wdev); + struct nlattr *tb[QCA_ATTR_WIL_MAX + 1]; + struct wil_nl_60g_send_receive_wmi *cmd; + struct wil_nl_60g_debug_force_wmi debug_force_wmi; + int rc, len; + u32 wil_nl_60g_cmd_type, publish; + + rc = nla_parse(tb, QCA_ATTR_WIL_MAX, data, data_len, + wil_nl_60g_policy, NULL); + if (rc) { + wil_err(wil, "Invalid nl_60g_cmd ATTR\n"); + return rc; + } + + if (!tb[WIL_ATTR_60G_CMD_TYPE]) { + wil_err(wil, "Invalid nl_60g_cmd type\n"); + return -EINVAL; + } + + wil_nl_60g_cmd_type = nla_get_u32(tb[WIL_ATTR_60G_CMD_TYPE]); + + switch (wil_nl_60g_cmd_type) { + case NL_60G_CMD_REGISTER: + if (!tb[WIL_ATTR_60G_BUF]) { + wil_err(wil, "Invalid nl_60g_cmd spec\n"); + return -EINVAL; + } + + len = nla_len(tb[WIL_ATTR_60G_BUF]); + if (len != sizeof(publish)) { + wil_err(wil, "cmd buffer wrong len %d\n", len); + return -EINVAL; + } + memcpy(&publish, nla_data(tb[WIL_ATTR_60G_BUF]), len); + wil->publish_nl_evt = publish; + + wil_dbg_wmi(wil, "Publish wmi event %s\n", + publish ? "enabled" : "disabled"); + break; + case NL_60G_CMD_DEBUG: + if (!tb[WIL_ATTR_60G_BUF]) { + wil_err(wil, "Invalid nl_60g_cmd spec\n"); + return -EINVAL; + } + + len = nla_len(tb[WIL_ATTR_60G_BUF]); + if (len < sizeof(struct wil_nl_60g_debug)) { + wil_err(wil, "cmd buffer too short %d\n", len); + return -EINVAL; + } + + memcpy(&debug_force_wmi, nla_data(tb[WIL_ATTR_60G_BUF]), + sizeof(struct wil_nl_60g_debug)); + + switch (debug_force_wmi.hdr.cmd_id) { + case NL_60G_DBG_FORCE_WMI_SEND: + if (len != sizeof(debug_force_wmi)) { + wil_err(wil, "cmd buffer wrong len %d\n", len); + return -EINVAL; + } + + memcpy(&debug_force_wmi, nla_data(tb[WIL_ATTR_60G_BUF]), + sizeof(debug_force_wmi)); + wil->force_wmi_send = debug_force_wmi.enable; + + wil_dbg_wmi(wil, "force sending wmi commands %d\n", + wil->force_wmi_send); + break; + default: + rc = -EINVAL; + wil_err(wil, "invalid debug_cmd id %d", + debug_force_wmi.hdr.cmd_id); + } + break; + case NL_60G_CMD_FW_WMI: + if (!tb[WIL_ATTR_60G_BUF]) { + wil_err(wil, "Invalid nl_60g_cmd spec\n"); + return -EINVAL; + } + + len = nla_len(tb[WIL_ATTR_60G_BUF]); + if (len < offsetof(struct wil_nl_60g_send_receive_wmi, buf)) { + wil_err(wil, "wmi cmd buffer too small\n"); + return -EINVAL; + } + + cmd = kmalloc(len, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + memcpy(cmd, nla_data(tb[WIL_ATTR_60G_BUF]), (unsigned int)len); + + wil_dbg_wmi(wil, "sending user-space command (0x%04x) [%d]\n", + cmd->cmd_id, cmd->buf_len); + + if (wil->force_wmi_send) + rc = wmi_force_send(wil, cmd->cmd_id, cmd->dev_id, + cmd->buf, cmd->buf_len); + else + rc = wmi_send(wil, cmd->cmd_id, cmd->dev_id, + cmd->buf, cmd->buf_len); + + kfree(cmd); + break; + default: + rc = -EINVAL; + wil_err(wil, "invalid nl_60g_cmd type %d", wil_nl_60g_cmd_type); + } + + return rc; +} + +void wil_nl_60g_receive_wmi_evt(struct wil6210_priv *wil, u8 *cmd, int len) +{ + struct sk_buff *vendor_event = NULL; + struct wil_nl_60g_event *evt; + struct wil_nl_60g_send_receive_wmi *wmi_buf; + struct wmi_cmd_hdr *wmi_hdr = (struct wmi_cmd_hdr *)cmd; + int data_len; + + if (!wil->publish_nl_evt) + return; + + wil_dbg_wmi(wil, "report wmi event to user-space (0x%04x) [%d]\n", + le16_to_cpu(wmi_hdr->command_id), len); + + data_len = len - sizeof(struct wmi_cmd_hdr); + + evt = kzalloc(sizeof(*evt) + sizeof(*wmi_buf) + data_len, GFP_KERNEL); + if (!evt) + return; + + evt->evt_type = NL_60G_EVT_FW_WMI; + evt->buf_len = sizeof(*wmi_buf) + data_len; + + wmi_buf = (struct wil_nl_60g_send_receive_wmi *)evt->buf; + + wmi_buf->cmd_id = le16_to_cpu(wmi_hdr->command_id); + wmi_buf->dev_id = wmi_hdr->mid; + wmi_buf->buf_len = data_len; + memcpy(wmi_buf->buf, cmd + sizeof(struct wmi_cmd_hdr), data_len); + + vendor_event = + cfg80211_vendor_event_alloc(wil_to_wiphy(wil), + NULL, + data_len + 4 + NLMSG_HDRLEN + + sizeof(*evt) + sizeof(*wmi_buf), + QCA_EVENT_UNSPEC_INDEX, + GFP_KERNEL); + if (!vendor_event) + goto out; + + if (nla_put(vendor_event, WIL_ATTR_60G_BUF, + sizeof(*evt) + sizeof(*wmi_buf) + data_len, evt)) { + wil_err(wil, "failed to fill WIL_ATTR_60G_BUF\n"); + goto out; + } + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); + +out: + kfree(evt); +} diff --git a/drivers/net/wireless/ath/wil6210/ftm.h b/drivers/net/wireless/ath/wil6210/ftm.h index ec9a1464a657..cb4a0c8f9890 100644 --- a/drivers/net/wireless/ath/wil6210/ftm.h +++ b/drivers/net/wireless/ath/wil6210/ftm.h @@ -401,6 +401,7 @@ enum qca_events_index { QCA_EVENT_FTM_MEAS_RESULT_INDEX, QCA_EVENT_FTM_SESSION_DONE_INDEX, QCA_EVENT_AOA_MEAS_RESULT_INDEX, + QCA_EVENT_UNSPEC_INDEX, }; /* measurement parameters. Specified for each peer as part diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index c0c4f913115d..8b1baa7b35db 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -1691,6 +1691,8 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) wmi_event_flush(wil); + wil->force_wmi_send = false; + flush_workqueue(wil->wq_service); flush_workqueue(wil->wmi_wq); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index aef6e90b0fef..74ec64e4bf53 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -1060,6 +1060,9 @@ struct wil6210_priv { u32 rgf_ucode_assert_code_addr; u32 iccm_base; + u8 publish_nl_evt; /* deliver WMI events to user space */ + u8 force_wmi_send; /* allow WMI command while FW in sysassert */ + /* relevant only for eDMA */ bool use_compressed_rx_status; u32 rx_status_ring_order; @@ -1251,6 +1254,8 @@ void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr); int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, struct wil6210_mbox_hdr *hdr); int wmi_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len); +int wmi_force_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, + u16 len); void wmi_recv_cmd(struct wil6210_priv *wil); int wmi_call(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len, u16 reply_id, void *reply, u16 reply_size, int to_msec); @@ -1444,6 +1449,7 @@ void wil_ftm_evt_per_dest_res(struct wil6210_vif *vif, void wil_aoa_evt_meas(struct wil6210_vif *vif, struct wmi_aoa_meas_event *evt, int len); +void wil_nl_60g_receive_wmi_evt(struct wil6210_priv *wil, u8 *cmd, int len); /* link loss */ int wmi_link_maintain_cfg_write(struct wil6210_priv *wil, const u8 *addr, diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 51978483f6d5..4898c4503059 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -631,7 +631,7 @@ static const char *eventid2name(u16 eventid) } static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, - void *buf, u16 len) + void *buf, u16 len, bool force_send) { struct { struct wil6210_mbox_hdr hdr; @@ -663,7 +663,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, might_sleep(); - if (!test_bit(wil_status_fwready, wil->status)) { + if (!test_bit(wil_status_fwready, wil->status) && !force_send) { wil_err(wil, "WMI: cannot send command while FW not ready\n"); return -EAGAIN; } @@ -702,7 +702,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, wil_dbg_wmi(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head); /* wait till FW finish with previous command */ for (retry = 5; retry > 0; retry--) { - if (!test_bit(wil_status_fwready, wil->status)) { + if (!test_bit(wil_status_fwready, wil->status) && !force_send) { wil_err(wil, "WMI: cannot send command while FW not ready\n"); rc = -EAGAIN; goto out; @@ -757,7 +757,19 @@ int wmi_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len) int rc; mutex_lock(&wil->wmi_mutex); - rc = __wmi_send(wil, cmdid, mid, buf, len); + rc = __wmi_send(wil, cmdid, mid, buf, len, false); + mutex_unlock(&wil->wmi_mutex); + + return rc; +} + +int wmi_force_send(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, + u16 len) +{ + int rc; + + mutex_lock(&wil->wmi_mutex); + rc = __wmi_send(wil, cmdid, mid, buf, len, true); mutex_unlock(&wil->wmi_mutex); return rc; @@ -1988,6 +2000,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil) u16 id = le16_to_cpu(wmi->command_id); u8 mid = wmi->mid; u32 tstamp = le32_to_cpu(wmi->fw_timestamp); + wil_nl_60g_receive_wmi_evt(wil, cmd, len); if (test_bit(wil_status_resuming, wil->status)) { if (id == WMI_TRAFFIC_RESUME_EVENTID) clear_bit(wil_status_resuming, @@ -2064,7 +2077,7 @@ int wmi_call(struct wil6210_priv *wil, u16 cmdid, u8 mid, void *buf, u16 len, reinit_completion(&wil->wmi_call); spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); - rc = __wmi_send(wil, cmdid, mid, buf, len); + rc = __wmi_send(wil, cmdid, mid, buf, len, false); if (rc) goto out;