Loading drivers/net/wireless/ath/wil6210/cfg80211.c +550 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include <linux/etherdevice.h> #include <linux/moduleparam.h> #include <net/netlink.h> #include "wil6210.h" #include "wmi.h" #include "ftm.h" Loading Loading @@ -56,6 +57,62 @@ static struct ieee80211_channel wil_60ghz_channels[] = { #define QCA_NL80211_VENDOR_ID 0x001374 #define WIL_MAX_RF_SECTORS (128) #define WIL_CID_ALL (0xff) enum qca_wlan_vendor_attr_rf_sector { QCA_ATTR_MAC_ADDR = 6, QCA_ATTR_PAD = 13, QCA_ATTR_TSF = 29, QCA_ATTR_DMG_RF_SECTOR_INDEX = 30, 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, }; enum qca_wlan_vendor_attr_dmg_rf_sector_type { QCA_ATTR_DMG_RF_SECTOR_TYPE_RX, QCA_ATTR_DMG_RF_SECTOR_TYPE_TX, QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX }; enum qca_wlan_vendor_attr_dmg_rf_sector_cfg { QCA_ATTR_DMG_RF_SECTOR_CFG_INVALID = 0, QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO, QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16, /* keep last */ QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST, QCA_ATTR_DMG_RF_SECTOR_CFG_MAX = QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1 }; static const struct nla_policy wil_rf_sector_policy[QCA_ATTR_DMG_RF_SECTOR_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 }, [QCA_ATTR_DMG_RF_MODULE_MASK] = { .type = NLA_U32 }, [QCA_ATTR_DMG_RF_SECTOR_CFG] = { .type = NLA_NESTED }, }; static const struct nla_policy wil_rf_sector_cfg_policy[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1] = { [QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] = { .type = NLA_U8 }, [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] = { .type = NLA_U32 }, [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] = { .type = NLA_U32 }, [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] = { .type = NLA_U32 }, [QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] = { .type = NLA_U32 }, [QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] = { .type = NLA_U32 }, [QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16] = { .type = NLA_U32 }, }; enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA = 128, QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION = 129, Loading @@ -66,8 +123,25 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS = 134, QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS = 135, QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT = 136, QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG = 139, 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, }; static int wil_rf_sector_get_cfg(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len); static int wil_rf_sector_set_cfg(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len); static int wil_rf_sector_get_selected(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len); static int wil_rf_sector_set_selected(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[] = { { Loading Loading @@ -112,6 +186,36 @@ static const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = { WIPHY_VENDOR_CMD_NEED_RUNNING, .doit = wil_aoa_abort_measurement }, { .info.vendor_id = QCA_NL80211_VENDOR_ID, .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, .doit = wil_rf_sector_get_cfg }, { .info.vendor_id = QCA_NL80211_VENDOR_ID, .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, .doit = wil_rf_sector_set_cfg }, { .info.vendor_id = QCA_NL80211_VENDOR_ID, .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, .doit = wil_rf_sector_get_selected }, { .info.vendor_id = QCA_NL80211_VENDOR_ID, .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, .doit = wil_rf_sector_set_selected }, }; /* vendor specific events */ Loading Loading @@ -1838,3 +1942,449 @@ void wil_p2p_wdev_free(struct wil6210_priv *wil) kfree(p2p_wdev); } } static int wil_rf_sector_status_to_rc(u8 status) { switch (status) { case WMI_RF_SECTOR_STATUS_SUCCESS: return 0; case WMI_RF_SECTOR_STATUS_BAD_PARAMETERS_ERROR: return -EINVAL; case WMI_RF_SECTOR_STATUS_BUSY_ERROR: return -EAGAIN; case WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR: return -EOPNOTSUPP; default: return -EINVAL; } } static int wil_rf_sector_get_cfg(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len) { struct wil6210_priv *wil = wdev_to_wil(wdev); int rc; struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; u16 sector_index; u8 sector_type; u32 rf_modules_vec; struct wmi_get_rf_sector_params_cmd cmd; struct { struct wmi_cmd_hdr wmi; struct wmi_get_rf_sector_params_done_event evt; } __packed reply; struct sk_buff *msg; struct nlattr *nl_cfgs, *nl_cfg; u32 i; struct wmi_rf_sector_info *si; if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) return -EOPNOTSUPP; rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len, wil_rf_sector_policy); if (rc) { wil_err(wil, "Invalid rf sector ATTR\n"); return rc; } if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] || !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] || !tb[QCA_ATTR_DMG_RF_MODULE_MASK]) { wil_err(wil, "Invalid rf sector spec\n"); return -EINVAL; } sector_index = nla_get_u16( tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]); if (sector_index >= WIL_MAX_RF_SECTORS) { wil_err(wil, "Invalid sector index %d\n", sector_index); return -EINVAL; } sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]); if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) { wil_err(wil, "Invalid sector type %d\n", sector_type); return -EINVAL; } rf_modules_vec = nla_get_u32( tb[QCA_ATTR_DMG_RF_MODULE_MASK]); if (rf_modules_vec >= BIT(WMI_MAX_RF_MODULES_NUM)) { wil_err(wil, "Invalid rf module mask 0x%x\n", rf_modules_vec); return -EINVAL; } cmd.sector_idx = cpu_to_le16(sector_index); cmd.sector_type = sector_type; cmd.rf_modules_vec = rf_modules_vec & 0xFF; memset(&reply, 0, sizeof(reply)); rc = wmi_call(wil, WMI_GET_RF_SECTOR_PARAMS_CMDID, &cmd, sizeof(cmd), WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID, &reply, sizeof(reply), 500); if (rc) return rc; if (reply.evt.status) { wil_err(wil, "get rf sector cfg failed with status %d\n", reply.evt.status); return wil_rf_sector_status_to_rc(reply.evt.status); } msg = cfg80211_vendor_cmd_alloc_reply_skb( wiphy, 64 * WMI_MAX_RF_MODULES_NUM); if (!msg) return -ENOMEM; if (nla_put_u64(msg, QCA_ATTR_TSF, le64_to_cpu(reply.evt.tsf))) goto nla_put_failure; nl_cfgs = nla_nest_start(msg, QCA_ATTR_DMG_RF_SECTOR_CFG); if (!nl_cfgs) goto nla_put_failure; for (i = 0; i < WMI_MAX_RF_MODULES_NUM; i++) { if (!(rf_modules_vec & BIT(i))) continue; nl_cfg = nla_nest_start(msg, i); if (!nl_cfg) goto nla_put_failure; si = &reply.evt.sectors_info[i]; if (nla_put_u8(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX, i) || nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0, le32_to_cpu(si->etype0)) || nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1, le32_to_cpu(si->etype1)) || nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2, le32_to_cpu(si->etype2)) || nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI, le32_to_cpu(si->psh_hi)) || nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO, le32_to_cpu(si->psh_lo)) || nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16, le32_to_cpu(si->dtype_swch_off))) goto nla_put_failure; nla_nest_end(msg, nl_cfg); } nla_nest_end(msg, nl_cfgs); rc = cfg80211_vendor_cmd_reply(msg); return rc; nla_put_failure: kfree_skb(msg); return -ENOBUFS; } static int wil_rf_sector_set_cfg(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len) { struct wil6210_priv *wil = wdev_to_wil(wdev); int rc, tmp; struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; struct nlattr *tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1]; u16 sector_index, rf_module_index; u8 sector_type; u32 rf_modules_vec = 0; struct wmi_set_rf_sector_params_cmd cmd; struct { struct wmi_cmd_hdr wmi; struct wmi_set_rf_sector_params_done_event evt; } __packed reply; struct nlattr *nl_cfg; struct wmi_rf_sector_info *si; if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) return -EOPNOTSUPP; rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len, wil_rf_sector_policy); if (rc) { wil_err(wil, "Invalid rf sector ATTR\n"); return rc; } if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] || !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] || !tb[QCA_ATTR_DMG_RF_SECTOR_CFG]) { wil_err(wil, "Invalid rf sector spec\n"); return -EINVAL; } sector_index = nla_get_u16( tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]); if (sector_index >= WIL_MAX_RF_SECTORS) { wil_err(wil, "Invalid sector index %d\n", sector_index); return -EINVAL; } sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]); if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) { wil_err(wil, "Invalid sector type %d\n", sector_type); return -EINVAL; } memset(&cmd, 0, sizeof(cmd)); cmd.sector_idx = cpu_to_le16(sector_index); cmd.sector_type = sector_type; nla_for_each_nested(nl_cfg, tb[QCA_ATTR_DMG_RF_SECTOR_CFG], tmp) { rc = nla_parse_nested(tb2, QCA_ATTR_DMG_RF_SECTOR_CFG_MAX, nl_cfg, wil_rf_sector_cfg_policy); if (rc) { wil_err(wil, "invalid sector cfg\n"); return -EINVAL; } if (!tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] || !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] || !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] || !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] || !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] || !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] || !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16]) { wil_err(wil, "missing cfg params\n"); return -EINVAL; } rf_module_index = nla_get_u8( tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX]); if (rf_module_index >= WMI_MAX_RF_MODULES_NUM) { wil_err(wil, "invalid RF module index %d\n", rf_module_index); return -EINVAL; } rf_modules_vec |= BIT(rf_module_index); si = &cmd.sectors_info[rf_module_index]; si->etype0 = cpu_to_le32(nla_get_u32( tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0])); si->etype1 = cpu_to_le32(nla_get_u32( tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1])); si->etype2 = cpu_to_le32(nla_get_u32( tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2])); si->psh_hi = cpu_to_le32(nla_get_u32( tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI])); si->psh_lo = cpu_to_le32(nla_get_u32( tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO])); si->dtype_swch_off = cpu_to_le32(nla_get_u32( tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16])); } cmd.rf_modules_vec = rf_modules_vec & 0xFF; memset(&reply, 0, sizeof(reply)); rc = wmi_call(wil, WMI_SET_RF_SECTOR_PARAMS_CMDID, &cmd, sizeof(cmd), WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID, &reply, sizeof(reply), 500); if (rc) return rc; return wil_rf_sector_status_to_rc(reply.evt.status); } static int wil_rf_sector_get_selected(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len) { struct wil6210_priv *wil = wdev_to_wil(wdev); int rc; struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; u8 sector_type, mac_addr[ETH_ALEN]; int cid = 0; struct wmi_get_selected_rf_sector_index_cmd cmd; struct { struct wmi_cmd_hdr wmi; struct wmi_get_selected_rf_sector_index_done_event evt; } __packed reply; struct sk_buff *msg; if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) return -EOPNOTSUPP; rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len, wil_rf_sector_policy); if (rc) { wil_err(wil, "Invalid rf sector ATTR\n"); return rc; } if (!tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) { wil_err(wil, "Invalid rf sector spec\n"); return -EINVAL; } sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]); if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) { wil_err(wil, "Invalid sector type %d\n", sector_type); return -EINVAL; } if (tb[QCA_ATTR_MAC_ADDR]) { ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR])); cid = wil_find_cid(wil, mac_addr); if (cid < 0) { wil_err(wil, "invalid MAC address %pM\n", mac_addr); return -ENOENT; } } else { if (test_bit(wil_status_fwconnected, wil->status)) { wil_err(wil, "must specify MAC address when connected\n"); return -EINVAL; } } memset(&cmd, 0, sizeof(cmd)); cmd.cid = (u8)cid; cmd.sector_type = sector_type; memset(&reply, 0, sizeof(reply)); rc = wmi_call(wil, WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID, &cmd, sizeof(cmd), WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID, &reply, sizeof(reply), 500); if (rc) return rc; if (reply.evt.status) { wil_err(wil, "get rf selected sector cfg failed with status %d\n", reply.evt.status); return wil_rf_sector_status_to_rc(reply.evt.status); } msg = cfg80211_vendor_cmd_alloc_reply_skb( wiphy, 64 * WMI_MAX_RF_MODULES_NUM); if (!msg) return -ENOMEM; if (nla_put_u64(msg, QCA_ATTR_TSF, le64_to_cpu(reply.evt.tsf)) || nla_put_u16(msg, QCA_ATTR_DMG_RF_SECTOR_INDEX, le16_to_cpu(reply.evt.sector_idx))) goto nla_put_failure; rc = cfg80211_vendor_cmd_reply(msg); return rc; nla_put_failure: kfree_skb(msg); return -ENOBUFS; } static int wil_rf_sector_wmi_set_selected(struct wil6210_priv *wil, u16 sector_index, u8 sector_type, u8 cid) { struct wmi_set_selected_rf_sector_index_cmd cmd; struct { struct wmi_cmd_hdr wmi; struct wmi_set_selected_rf_sector_index_done_event evt; } __packed reply; int rc; memset(&cmd, 0, sizeof(cmd)); cmd.sector_idx = cpu_to_le16(sector_index); cmd.sector_type = sector_type; cmd.cid = (u8)cid; memset(&reply, 0, sizeof(reply)); rc = wmi_call(wil, WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID, &cmd, sizeof(cmd), WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID, &reply, sizeof(reply), 500); if (rc) return rc; return wil_rf_sector_status_to_rc(reply.evt.status); } static int wil_rf_sector_set_selected(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len) { struct wil6210_priv *wil = wdev_to_wil(wdev); int rc; struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; u16 sector_index; u8 sector_type, mac_addr[ETH_ALEN], i; int cid = 0; if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) return -EOPNOTSUPP; rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len, wil_rf_sector_policy); if (rc) { wil_err(wil, "Invalid rf sector ATTR\n"); return rc; } if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] || !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) { wil_err(wil, "Invalid rf sector spec\n"); return -EINVAL; } sector_index = nla_get_u16( tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]); if (sector_index >= WIL_MAX_RF_SECTORS && sector_index != WMI_INVALID_RF_SECTOR_INDEX) { wil_err(wil, "Invalid sector index %d\n", sector_index); return -EINVAL; } sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]); if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) { wil_err(wil, "Invalid sector type %d\n", sector_type); return -EINVAL; } if (tb[QCA_ATTR_MAC_ADDR]) { ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR])); if (!is_broadcast_ether_addr(mac_addr)) { cid = wil_find_cid(wil, mac_addr); if (cid < 0) { wil_err(wil, "invalid MAC address %pM\n", mac_addr); return -ENOENT; } } else { if (sector_index != WMI_INVALID_RF_SECTOR_INDEX) { wil_err(wil, "broadcast MAC valid only with unlocking\n"); return -EINVAL; } cid = -1; } } else { if (test_bit(wil_status_fwconnected, wil->status)) { wil_err(wil, "must specify MAC address when connected\n"); return -EINVAL; } /* otherwise, using cid=0 for unassociated station */ } if (cid >= 0) { rc = wil_rf_sector_wmi_set_selected(wil, sector_index, sector_type, cid); } else { /* unlock all cids */ rc = wil_rf_sector_wmi_set_selected( wil, WMI_INVALID_RF_SECTOR_INDEX, sector_type, WIL_CID_ALL); if (rc == -EINVAL) { for (i = 0; i < WIL6210_MAX_CID; i++) { rc = wil_rf_sector_wmi_set_selected( wil, WMI_INVALID_RF_SECTOR_INDEX, sector_type, i); /* the FW will silently ignore and return * success for unused cid, so abort the loop * on any other error */ if (rc) { wil_err(wil, "unlock cid %d failed with status %d\n", i, rc); break; } } } } return rc; } drivers/net/wireless/ath/wil6210/ioctl.c +2 −2 Original line number Diff line number Diff line /* * Copyright (c) 2014 Qualcomm Atheros, Inc. * Copyright (c) 2014,2017 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above Loading Loading @@ -54,7 +54,7 @@ static void __iomem *wil_ioc_addr(struct wil6210_priv *wil, uint32_t addr, } off = a - wil->csr; if (size >= WIL6210_MEM_SIZE - off) { if (size >= wil->bar_size - off) { wil_err(wil, "Requested block does not fit into memory: " "off = 0x%08x size = 0x%08x\n", off, size); return NULL; Loading drivers/net/wireless/ath/wil6210/pcie_bus.c +10 −7 Original line number Diff line number Diff line Loading @@ -198,16 +198,18 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) .ramdump = wil_platform_rop_ramdump, .fw_recovery = wil_platform_rop_fw_recovery, }; u32 bar_size = pci_resource_len(pdev, 0); /* check HW */ dev_info(&pdev->dev, WIL_NAME " device found [%04x:%04x] (rev %x)\n", (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) { dev_err(&pdev->dev, "Not " WIL_NAME "? " "BAR0 size is %lu while expecting %lu\n", (ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE); " device found [%04x:%04x] (rev %x) bar size 0x%x\n", (int)pdev->vendor, (int)pdev->device, (int)pdev->revision, bar_size); if ((bar_size < WIL6210_MIN_MEM_SIZE) || (bar_size > WIL6210_MAX_MEM_SIZE)) { dev_err(&pdev->dev, "Unexpected BAR0 size 0x%x\n", bar_size); return -ENODEV; } Loading @@ -220,6 +222,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) wil->pdev = pdev; pci_set_drvdata(pdev, wil); wil->bar_size = bar_size; /* rollback to if_free */ wil->platform_handle = Loading drivers/net/wireless/ath/wil6210/wil6210.h +3 −1 Original line number Diff line number Diff line Loading @@ -59,7 +59,8 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) return (x >> b0) & ((1 << (b1 - b0 + 1)) - 1); } #define WIL6210_MEM_SIZE (2*1024*1024UL) #define WIL6210_MIN_MEM_SIZE (2 * 1024 * 1024UL) #define WIL6210_MAX_MEM_SIZE (4 * 1024 * 1024UL) #define WIL_TX_Q_LEN_DEFAULT (4000) #define WIL_RX_RING_SIZE_ORDER_DEFAULT (10) Loading Loading @@ -620,6 +621,7 @@ extern u8 led_polarity; struct wil6210_priv { struct pci_dev *pdev; u32 bar_size; struct wireless_dev *wdev; void __iomem *csr; DECLARE_BITMAP(status, wil_status_last); Loading drivers/net/wireless/ath/wil6210/wmi.c +2 −2 Original line number Diff line number Diff line Loading @@ -160,7 +160,7 @@ void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_) return NULL; off = HOSTADDR(ptr); if (off > WIL6210_MEM_SIZE - 4) if (off > wil->bar_size - 4) return NULL; return wil->csr + off; Loading @@ -180,7 +180,7 @@ void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr) return NULL; off = HOSTADDR(ptr); if (off > WIL6210_MEM_SIZE - 4) if (off > wil->bar_size - 4) return NULL; return wil->csr + off; Loading Loading
drivers/net/wireless/ath/wil6210/cfg80211.c +550 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include <linux/etherdevice.h> #include <linux/moduleparam.h> #include <net/netlink.h> #include "wil6210.h" #include "wmi.h" #include "ftm.h" Loading Loading @@ -56,6 +57,62 @@ static struct ieee80211_channel wil_60ghz_channels[] = { #define QCA_NL80211_VENDOR_ID 0x001374 #define WIL_MAX_RF_SECTORS (128) #define WIL_CID_ALL (0xff) enum qca_wlan_vendor_attr_rf_sector { QCA_ATTR_MAC_ADDR = 6, QCA_ATTR_PAD = 13, QCA_ATTR_TSF = 29, QCA_ATTR_DMG_RF_SECTOR_INDEX = 30, 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, }; enum qca_wlan_vendor_attr_dmg_rf_sector_type { QCA_ATTR_DMG_RF_SECTOR_TYPE_RX, QCA_ATTR_DMG_RF_SECTOR_TYPE_TX, QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX }; enum qca_wlan_vendor_attr_dmg_rf_sector_cfg { QCA_ATTR_DMG_RF_SECTOR_CFG_INVALID = 0, QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO, QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16, /* keep last */ QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST, QCA_ATTR_DMG_RF_SECTOR_CFG_MAX = QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1 }; static const struct nla_policy wil_rf_sector_policy[QCA_ATTR_DMG_RF_SECTOR_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 }, [QCA_ATTR_DMG_RF_MODULE_MASK] = { .type = NLA_U32 }, [QCA_ATTR_DMG_RF_SECTOR_CFG] = { .type = NLA_NESTED }, }; static const struct nla_policy wil_rf_sector_cfg_policy[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1] = { [QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] = { .type = NLA_U8 }, [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] = { .type = NLA_U32 }, [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] = { .type = NLA_U32 }, [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] = { .type = NLA_U32 }, [QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] = { .type = NLA_U32 }, [QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] = { .type = NLA_U32 }, [QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16] = { .type = NLA_U32 }, }; enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA = 128, QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION = 129, Loading @@ -66,8 +123,25 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS = 134, QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS = 135, QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT = 136, QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG = 139, 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, }; static int wil_rf_sector_get_cfg(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len); static int wil_rf_sector_set_cfg(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len); static int wil_rf_sector_get_selected(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len); static int wil_rf_sector_set_selected(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[] = { { Loading Loading @@ -112,6 +186,36 @@ static const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = { WIPHY_VENDOR_CMD_NEED_RUNNING, .doit = wil_aoa_abort_measurement }, { .info.vendor_id = QCA_NL80211_VENDOR_ID, .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, .doit = wil_rf_sector_get_cfg }, { .info.vendor_id = QCA_NL80211_VENDOR_ID, .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, .doit = wil_rf_sector_set_cfg }, { .info.vendor_id = QCA_NL80211_VENDOR_ID, .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, .doit = wil_rf_sector_get_selected }, { .info.vendor_id = QCA_NL80211_VENDOR_ID, .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, .doit = wil_rf_sector_set_selected }, }; /* vendor specific events */ Loading Loading @@ -1838,3 +1942,449 @@ void wil_p2p_wdev_free(struct wil6210_priv *wil) kfree(p2p_wdev); } } static int wil_rf_sector_status_to_rc(u8 status) { switch (status) { case WMI_RF_SECTOR_STATUS_SUCCESS: return 0; case WMI_RF_SECTOR_STATUS_BAD_PARAMETERS_ERROR: return -EINVAL; case WMI_RF_SECTOR_STATUS_BUSY_ERROR: return -EAGAIN; case WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR: return -EOPNOTSUPP; default: return -EINVAL; } } static int wil_rf_sector_get_cfg(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len) { struct wil6210_priv *wil = wdev_to_wil(wdev); int rc; struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; u16 sector_index; u8 sector_type; u32 rf_modules_vec; struct wmi_get_rf_sector_params_cmd cmd; struct { struct wmi_cmd_hdr wmi; struct wmi_get_rf_sector_params_done_event evt; } __packed reply; struct sk_buff *msg; struct nlattr *nl_cfgs, *nl_cfg; u32 i; struct wmi_rf_sector_info *si; if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) return -EOPNOTSUPP; rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len, wil_rf_sector_policy); if (rc) { wil_err(wil, "Invalid rf sector ATTR\n"); return rc; } if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] || !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] || !tb[QCA_ATTR_DMG_RF_MODULE_MASK]) { wil_err(wil, "Invalid rf sector spec\n"); return -EINVAL; } sector_index = nla_get_u16( tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]); if (sector_index >= WIL_MAX_RF_SECTORS) { wil_err(wil, "Invalid sector index %d\n", sector_index); return -EINVAL; } sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]); if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) { wil_err(wil, "Invalid sector type %d\n", sector_type); return -EINVAL; } rf_modules_vec = nla_get_u32( tb[QCA_ATTR_DMG_RF_MODULE_MASK]); if (rf_modules_vec >= BIT(WMI_MAX_RF_MODULES_NUM)) { wil_err(wil, "Invalid rf module mask 0x%x\n", rf_modules_vec); return -EINVAL; } cmd.sector_idx = cpu_to_le16(sector_index); cmd.sector_type = sector_type; cmd.rf_modules_vec = rf_modules_vec & 0xFF; memset(&reply, 0, sizeof(reply)); rc = wmi_call(wil, WMI_GET_RF_SECTOR_PARAMS_CMDID, &cmd, sizeof(cmd), WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID, &reply, sizeof(reply), 500); if (rc) return rc; if (reply.evt.status) { wil_err(wil, "get rf sector cfg failed with status %d\n", reply.evt.status); return wil_rf_sector_status_to_rc(reply.evt.status); } msg = cfg80211_vendor_cmd_alloc_reply_skb( wiphy, 64 * WMI_MAX_RF_MODULES_NUM); if (!msg) return -ENOMEM; if (nla_put_u64(msg, QCA_ATTR_TSF, le64_to_cpu(reply.evt.tsf))) goto nla_put_failure; nl_cfgs = nla_nest_start(msg, QCA_ATTR_DMG_RF_SECTOR_CFG); if (!nl_cfgs) goto nla_put_failure; for (i = 0; i < WMI_MAX_RF_MODULES_NUM; i++) { if (!(rf_modules_vec & BIT(i))) continue; nl_cfg = nla_nest_start(msg, i); if (!nl_cfg) goto nla_put_failure; si = &reply.evt.sectors_info[i]; if (nla_put_u8(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX, i) || nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0, le32_to_cpu(si->etype0)) || nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1, le32_to_cpu(si->etype1)) || nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2, le32_to_cpu(si->etype2)) || nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI, le32_to_cpu(si->psh_hi)) || nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO, le32_to_cpu(si->psh_lo)) || nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16, le32_to_cpu(si->dtype_swch_off))) goto nla_put_failure; nla_nest_end(msg, nl_cfg); } nla_nest_end(msg, nl_cfgs); rc = cfg80211_vendor_cmd_reply(msg); return rc; nla_put_failure: kfree_skb(msg); return -ENOBUFS; } static int wil_rf_sector_set_cfg(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len) { struct wil6210_priv *wil = wdev_to_wil(wdev); int rc, tmp; struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; struct nlattr *tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1]; u16 sector_index, rf_module_index; u8 sector_type; u32 rf_modules_vec = 0; struct wmi_set_rf_sector_params_cmd cmd; struct { struct wmi_cmd_hdr wmi; struct wmi_set_rf_sector_params_done_event evt; } __packed reply; struct nlattr *nl_cfg; struct wmi_rf_sector_info *si; if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) return -EOPNOTSUPP; rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len, wil_rf_sector_policy); if (rc) { wil_err(wil, "Invalid rf sector ATTR\n"); return rc; } if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] || !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] || !tb[QCA_ATTR_DMG_RF_SECTOR_CFG]) { wil_err(wil, "Invalid rf sector spec\n"); return -EINVAL; } sector_index = nla_get_u16( tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]); if (sector_index >= WIL_MAX_RF_SECTORS) { wil_err(wil, "Invalid sector index %d\n", sector_index); return -EINVAL; } sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]); if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) { wil_err(wil, "Invalid sector type %d\n", sector_type); return -EINVAL; } memset(&cmd, 0, sizeof(cmd)); cmd.sector_idx = cpu_to_le16(sector_index); cmd.sector_type = sector_type; nla_for_each_nested(nl_cfg, tb[QCA_ATTR_DMG_RF_SECTOR_CFG], tmp) { rc = nla_parse_nested(tb2, QCA_ATTR_DMG_RF_SECTOR_CFG_MAX, nl_cfg, wil_rf_sector_cfg_policy); if (rc) { wil_err(wil, "invalid sector cfg\n"); return -EINVAL; } if (!tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] || !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] || !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] || !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] || !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] || !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] || !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16]) { wil_err(wil, "missing cfg params\n"); return -EINVAL; } rf_module_index = nla_get_u8( tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX]); if (rf_module_index >= WMI_MAX_RF_MODULES_NUM) { wil_err(wil, "invalid RF module index %d\n", rf_module_index); return -EINVAL; } rf_modules_vec |= BIT(rf_module_index); si = &cmd.sectors_info[rf_module_index]; si->etype0 = cpu_to_le32(nla_get_u32( tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0])); si->etype1 = cpu_to_le32(nla_get_u32( tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1])); si->etype2 = cpu_to_le32(nla_get_u32( tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2])); si->psh_hi = cpu_to_le32(nla_get_u32( tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI])); si->psh_lo = cpu_to_le32(nla_get_u32( tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO])); si->dtype_swch_off = cpu_to_le32(nla_get_u32( tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16])); } cmd.rf_modules_vec = rf_modules_vec & 0xFF; memset(&reply, 0, sizeof(reply)); rc = wmi_call(wil, WMI_SET_RF_SECTOR_PARAMS_CMDID, &cmd, sizeof(cmd), WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID, &reply, sizeof(reply), 500); if (rc) return rc; return wil_rf_sector_status_to_rc(reply.evt.status); } static int wil_rf_sector_get_selected(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len) { struct wil6210_priv *wil = wdev_to_wil(wdev); int rc; struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; u8 sector_type, mac_addr[ETH_ALEN]; int cid = 0; struct wmi_get_selected_rf_sector_index_cmd cmd; struct { struct wmi_cmd_hdr wmi; struct wmi_get_selected_rf_sector_index_done_event evt; } __packed reply; struct sk_buff *msg; if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) return -EOPNOTSUPP; rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len, wil_rf_sector_policy); if (rc) { wil_err(wil, "Invalid rf sector ATTR\n"); return rc; } if (!tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) { wil_err(wil, "Invalid rf sector spec\n"); return -EINVAL; } sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]); if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) { wil_err(wil, "Invalid sector type %d\n", sector_type); return -EINVAL; } if (tb[QCA_ATTR_MAC_ADDR]) { ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR])); cid = wil_find_cid(wil, mac_addr); if (cid < 0) { wil_err(wil, "invalid MAC address %pM\n", mac_addr); return -ENOENT; } } else { if (test_bit(wil_status_fwconnected, wil->status)) { wil_err(wil, "must specify MAC address when connected\n"); return -EINVAL; } } memset(&cmd, 0, sizeof(cmd)); cmd.cid = (u8)cid; cmd.sector_type = sector_type; memset(&reply, 0, sizeof(reply)); rc = wmi_call(wil, WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID, &cmd, sizeof(cmd), WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID, &reply, sizeof(reply), 500); if (rc) return rc; if (reply.evt.status) { wil_err(wil, "get rf selected sector cfg failed with status %d\n", reply.evt.status); return wil_rf_sector_status_to_rc(reply.evt.status); } msg = cfg80211_vendor_cmd_alloc_reply_skb( wiphy, 64 * WMI_MAX_RF_MODULES_NUM); if (!msg) return -ENOMEM; if (nla_put_u64(msg, QCA_ATTR_TSF, le64_to_cpu(reply.evt.tsf)) || nla_put_u16(msg, QCA_ATTR_DMG_RF_SECTOR_INDEX, le16_to_cpu(reply.evt.sector_idx))) goto nla_put_failure; rc = cfg80211_vendor_cmd_reply(msg); return rc; nla_put_failure: kfree_skb(msg); return -ENOBUFS; } static int wil_rf_sector_wmi_set_selected(struct wil6210_priv *wil, u16 sector_index, u8 sector_type, u8 cid) { struct wmi_set_selected_rf_sector_index_cmd cmd; struct { struct wmi_cmd_hdr wmi; struct wmi_set_selected_rf_sector_index_done_event evt; } __packed reply; int rc; memset(&cmd, 0, sizeof(cmd)); cmd.sector_idx = cpu_to_le16(sector_index); cmd.sector_type = sector_type; cmd.cid = (u8)cid; memset(&reply, 0, sizeof(reply)); rc = wmi_call(wil, WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID, &cmd, sizeof(cmd), WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID, &reply, sizeof(reply), 500); if (rc) return rc; return wil_rf_sector_status_to_rc(reply.evt.status); } static int wil_rf_sector_set_selected(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int data_len) { struct wil6210_priv *wil = wdev_to_wil(wdev); int rc; struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; u16 sector_index; u8 sector_type, mac_addr[ETH_ALEN], i; int cid = 0; if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) return -EOPNOTSUPP; rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len, wil_rf_sector_policy); if (rc) { wil_err(wil, "Invalid rf sector ATTR\n"); return rc; } if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] || !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) { wil_err(wil, "Invalid rf sector spec\n"); return -EINVAL; } sector_index = nla_get_u16( tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]); if (sector_index >= WIL_MAX_RF_SECTORS && sector_index != WMI_INVALID_RF_SECTOR_INDEX) { wil_err(wil, "Invalid sector index %d\n", sector_index); return -EINVAL; } sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]); if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) { wil_err(wil, "Invalid sector type %d\n", sector_type); return -EINVAL; } if (tb[QCA_ATTR_MAC_ADDR]) { ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR])); if (!is_broadcast_ether_addr(mac_addr)) { cid = wil_find_cid(wil, mac_addr); if (cid < 0) { wil_err(wil, "invalid MAC address %pM\n", mac_addr); return -ENOENT; } } else { if (sector_index != WMI_INVALID_RF_SECTOR_INDEX) { wil_err(wil, "broadcast MAC valid only with unlocking\n"); return -EINVAL; } cid = -1; } } else { if (test_bit(wil_status_fwconnected, wil->status)) { wil_err(wil, "must specify MAC address when connected\n"); return -EINVAL; } /* otherwise, using cid=0 for unassociated station */ } if (cid >= 0) { rc = wil_rf_sector_wmi_set_selected(wil, sector_index, sector_type, cid); } else { /* unlock all cids */ rc = wil_rf_sector_wmi_set_selected( wil, WMI_INVALID_RF_SECTOR_INDEX, sector_type, WIL_CID_ALL); if (rc == -EINVAL) { for (i = 0; i < WIL6210_MAX_CID; i++) { rc = wil_rf_sector_wmi_set_selected( wil, WMI_INVALID_RF_SECTOR_INDEX, sector_type, i); /* the FW will silently ignore and return * success for unused cid, so abort the loop * on any other error */ if (rc) { wil_err(wil, "unlock cid %d failed with status %d\n", i, rc); break; } } } } return rc; }
drivers/net/wireless/ath/wil6210/ioctl.c +2 −2 Original line number Diff line number Diff line /* * Copyright (c) 2014 Qualcomm Atheros, Inc. * Copyright (c) 2014,2017 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above Loading Loading @@ -54,7 +54,7 @@ static void __iomem *wil_ioc_addr(struct wil6210_priv *wil, uint32_t addr, } off = a - wil->csr; if (size >= WIL6210_MEM_SIZE - off) { if (size >= wil->bar_size - off) { wil_err(wil, "Requested block does not fit into memory: " "off = 0x%08x size = 0x%08x\n", off, size); return NULL; Loading
drivers/net/wireless/ath/wil6210/pcie_bus.c +10 −7 Original line number Diff line number Diff line Loading @@ -198,16 +198,18 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) .ramdump = wil_platform_rop_ramdump, .fw_recovery = wil_platform_rop_fw_recovery, }; u32 bar_size = pci_resource_len(pdev, 0); /* check HW */ dev_info(&pdev->dev, WIL_NAME " device found [%04x:%04x] (rev %x)\n", (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) { dev_err(&pdev->dev, "Not " WIL_NAME "? " "BAR0 size is %lu while expecting %lu\n", (ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE); " device found [%04x:%04x] (rev %x) bar size 0x%x\n", (int)pdev->vendor, (int)pdev->device, (int)pdev->revision, bar_size); if ((bar_size < WIL6210_MIN_MEM_SIZE) || (bar_size > WIL6210_MAX_MEM_SIZE)) { dev_err(&pdev->dev, "Unexpected BAR0 size 0x%x\n", bar_size); return -ENODEV; } Loading @@ -220,6 +222,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) wil->pdev = pdev; pci_set_drvdata(pdev, wil); wil->bar_size = bar_size; /* rollback to if_free */ wil->platform_handle = Loading
drivers/net/wireless/ath/wil6210/wil6210.h +3 −1 Original line number Diff line number Diff line Loading @@ -59,7 +59,8 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) return (x >> b0) & ((1 << (b1 - b0 + 1)) - 1); } #define WIL6210_MEM_SIZE (2*1024*1024UL) #define WIL6210_MIN_MEM_SIZE (2 * 1024 * 1024UL) #define WIL6210_MAX_MEM_SIZE (4 * 1024 * 1024UL) #define WIL_TX_Q_LEN_DEFAULT (4000) #define WIL_RX_RING_SIZE_ORDER_DEFAULT (10) Loading Loading @@ -620,6 +621,7 @@ extern u8 led_polarity; struct wil6210_priv { struct pci_dev *pdev; u32 bar_size; struct wireless_dev *wdev; void __iomem *csr; DECLARE_BITMAP(status, wil_status_last); Loading
drivers/net/wireless/ath/wil6210/wmi.c +2 −2 Original line number Diff line number Diff line Loading @@ -160,7 +160,7 @@ void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_) return NULL; off = HOSTADDR(ptr); if (off > WIL6210_MEM_SIZE - 4) if (off > wil->bar_size - 4) return NULL; return wil->csr + off; Loading @@ -180,7 +180,7 @@ void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr) return NULL; off = HOSTADDR(ptr); if (off > WIL6210_MEM_SIZE - 4) if (off > wil->bar_size - 4) return NULL; return wil->csr + off; Loading