developer | 064da3c | 2023-06-13 15:57:26 +0800 | [diff] [blame] | 1 | From 2637da59e92d101889cf3680e57f0594c6b349ec Mon Sep 17 00:00:00 2001 |
| 2 | From: Evelyn Tsai <evelyn.tsai@mediatek.com> |
| 3 | Date: Wed, 26 Apr 2023 04:40:05 +0800 |
| 4 | Subject: [PATCH 1003/1015] wifi: mt76: mt7996: Add air monitor support |
| 5 | |
| 6 | --- |
| 7 | mt76_connac_mcu.h | 1 + |
| 8 | mt7996/mac.c | 4 + |
| 9 | mt7996/main.c | 4 + |
| 10 | mt7996/mt7996.h | 35 +++++ |
| 11 | mt7996/vendor.c | 362 ++++++++++++++++++++++++++++++++++++++++++++++ |
| 12 | mt7996/vendor.h | 39 +++++ |
| 13 | 6 files changed, 445 insertions(+) |
| 14 | |
| 15 | diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h |
| 16 | index 4bb9508a..e62f17ad 100644 |
| 17 | --- a/mt76_connac_mcu.h |
| 18 | +++ b/mt76_connac_mcu.h |
| 19 | @@ -1206,6 +1206,7 @@ enum { |
| 20 | MCU_UNI_CMD_REG_ACCESS = 0x0d, |
| 21 | MCU_UNI_CMD_CHIP_CONFIG = 0x0e, |
| 22 | MCU_UNI_CMD_POWER_CTRL = 0x0f, |
| 23 | + MCU_UNI_CMD_CFG_SMESH = 0x10, |
| 24 | MCU_UNI_CMD_RX_HDR_TRANS = 0x12, |
| 25 | MCU_UNI_CMD_SER = 0x13, |
| 26 | MCU_UNI_CMD_TWT = 0x14, |
| 27 | diff --git a/mt7996/mac.c b/mt7996/mac.c |
| 28 | index 05269e7f..3dc5cdae 100644 |
| 29 | --- a/mt7996/mac.c |
| 30 | +++ b/mt7996/mac.c |
| 31 | @@ -865,6 +865,10 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, struct sk_buff *skb) |
| 32 | if (ieee80211_has_a4(fc) && is_mesh && status->amsdu) |
| 33 | *qos &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; |
| 34 | } |
| 35 | +#ifdef CONFIG_MTK_VENDOR |
| 36 | + if (phy->amnt_ctrl.enable && !ieee80211_is_beacon(fc)) |
| 37 | + mt7996_vendor_amnt_fill_rx(phy, skb); |
| 38 | +#endif |
| 39 | } else { |
| 40 | status->flag |= RX_FLAG_8023; |
| 41 | } |
| 42 | diff --git a/mt7996/main.c b/mt7996/main.c |
| 43 | index 2ed66e6c..e5627c96 100644 |
| 44 | --- a/mt7996/main.c |
| 45 | +++ b/mt7996/main.c |
| 46 | @@ -672,6 +672,10 @@ int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, |
| 47 | mt7996_mac_wtbl_update(dev, idx, |
| 48 | MT_WTBL_UPDATE_ADM_COUNT_CLEAR); |
| 49 | |
| 50 | +#ifdef CONFIG_MTK_VENDOR |
| 51 | + mt7996_vendor_amnt_sta_remove(mvif->phy, sta); |
| 52 | +#endif |
| 53 | + |
| 54 | ret = mt7996_mcu_add_sta(dev, vif, sta, true); |
| 55 | if (ret) |
| 56 | return ret; |
| 57 | diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h |
| 58 | index b9f3dd8e..dc44edc1 100644 |
| 59 | --- a/mt7996/mt7996.h |
| 60 | +++ b/mt7996/mt7996.h |
| 61 | @@ -235,6 +235,34 @@ enum { |
| 62 | SCS_ENABLE, |
| 63 | }; |
| 64 | |
| 65 | +#ifdef CONFIG_MTK_VENDOR |
| 66 | +#define MT7996_AIR_MONITOR_MAX_ENTRY 16 |
| 67 | +#define MT7996_AIR_MONITOR_MAX_GROUP (MT7996_AIR_MONITOR_MAX_ENTRY >> 1) |
| 68 | + |
| 69 | +struct mt7996_air_monitor_group { |
| 70 | + bool enable; |
| 71 | + bool used[2]; |
| 72 | +}; |
| 73 | + |
| 74 | +struct mt7996_air_monitor_entry { |
| 75 | + bool enable; |
| 76 | + |
| 77 | + u8 group_idx; |
| 78 | + u8 group_used_idx; |
| 79 | + u8 muar_idx; |
| 80 | + u8 addr[ETH_ALEN]; |
| 81 | + u32 last_seen; |
| 82 | + s8 rssi[4]; |
| 83 | + struct ieee80211_sta *sta; |
| 84 | +}; |
| 85 | + |
| 86 | +struct mt7996_air_monitor_ctrl { |
| 87 | + u8 enable; |
| 88 | + struct mt7996_air_monitor_group group[MT7996_AIR_MONITOR_MAX_GROUP]; |
| 89 | + struct mt7996_air_monitor_entry entry[MT7996_AIR_MONITOR_MAX_ENTRY]; |
| 90 | +}; |
| 91 | +#endif |
| 92 | + |
| 93 | struct mt7996_phy { |
| 94 | struct mt76_phy *mt76; |
| 95 | struct mt7996_dev *dev; |
| 96 | @@ -285,6 +313,10 @@ struct mt7996_phy { |
| 97 | u8 spe_idx; |
| 98 | } test; |
| 99 | #endif |
| 100 | +#ifdef CONFIG_MTK_VENDOR |
| 101 | + spinlock_t amnt_lock; |
| 102 | + struct mt7996_air_monitor_ctrl amnt_ctrl; |
| 103 | +#endif |
| 104 | }; |
| 105 | |
| 106 | struct mt7996_dev { |
| 107 | @@ -677,6 +709,9 @@ void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, |
| 108 | #ifdef CONFIG_MTK_VENDOR |
| 109 | void mt7996_set_wireless_vif(void *data, u8 *mac, struct ieee80211_vif *vif); |
| 110 | void mt7996_vendor_register(struct mt7996_phy *phy); |
| 111 | +void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb); |
| 112 | +int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy, |
| 113 | + struct ieee80211_sta *sta); |
| 114 | #endif |
| 115 | |
| 116 | #ifdef CONFIG_MTK_DEBUG |
| 117 | diff --git a/mt7996/vendor.c b/mt7996/vendor.c |
| 118 | index 08ecc2b3..8a021324 100644 |
| 119 | --- a/mt7996/vendor.c |
| 120 | +++ b/mt7996/vendor.c |
| 121 | @@ -15,6 +15,32 @@ mu_ctrl_policy[NUM_MTK_VENDOR_ATTRS_MU_CTRL] = { |
| 122 | [MTK_VENDOR_ATTR_MU_CTRL_DUMP] = {.type = NLA_U8 }, |
| 123 | }; |
| 124 | |
| 125 | +static const struct nla_policy |
| 126 | +amnt_ctrl_policy[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL] = { |
| 127 | + [MTK_VENDOR_ATTR_AMNT_CTRL_SET] = {.type = NLA_NESTED }, |
| 128 | + [MTK_VENDOR_ATTR_AMNT_CTRL_DUMP] = { .type = NLA_NESTED }, |
| 129 | +}; |
| 130 | + |
| 131 | +static const struct nla_policy |
| 132 | +amnt_set_policy[NUM_MTK_VENDOR_ATTRS_AMNT_SET] = { |
| 133 | + [MTK_VENDOR_ATTR_AMNT_SET_INDEX] = {.type = NLA_U8 }, |
| 134 | + [MTK_VENDOR_ATTR_AMNT_SET_MACADDR] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), |
| 135 | +}; |
| 136 | + |
| 137 | +static const struct nla_policy |
| 138 | +amnt_dump_policy[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP] = { |
| 139 | + [MTK_VENDOR_ATTR_AMNT_DUMP_INDEX] = {.type = NLA_U8 }, |
| 140 | + [MTK_VENDOR_ATTR_AMNT_DUMP_LEN] = { .type = NLA_U8 }, |
| 141 | + [MTK_VENDOR_ATTR_AMNT_DUMP_RESULT] = { .type = NLA_NESTED }, |
| 142 | +}; |
| 143 | + |
| 144 | +struct mt7996_amnt_data { |
| 145 | + u8 idx; |
| 146 | + u8 addr[ETH_ALEN]; |
| 147 | + s8 rssi[4]; |
| 148 | + u32 last_seen; |
| 149 | +}; |
| 150 | + |
| 151 | static int mt7996_vendor_mu_ctrl(struct wiphy *wiphy, |
| 152 | struct wireless_dev *wdev, |
| 153 | const void *data, |
| 154 | @@ -62,6 +88,328 @@ mt7996_vendor_mu_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev, |
| 155 | return len; |
| 156 | } |
| 157 | |
| 158 | +void mt7996_vendor_amnt_fill_rx(struct mt7996_phy *phy, struct sk_buff *skb) |
| 159 | +{ |
| 160 | + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; |
| 161 | + struct mt7996_air_monitor_ctrl *ctrl = &phy->amnt_ctrl; |
| 162 | + struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb); |
| 163 | + __le16 fc = hdr->frame_control; |
| 164 | + u8 addr[ETH_ALEN]; |
| 165 | + int i; |
| 166 | + |
| 167 | + if (!ieee80211_has_fromds(fc)) |
| 168 | + ether_addr_copy(addr, hdr->addr2); |
| 169 | + else if (ieee80211_has_tods(fc)) |
| 170 | + ether_addr_copy(addr, hdr->addr4); |
| 171 | + else |
| 172 | + ether_addr_copy(addr, hdr->addr3); |
| 173 | + |
| 174 | + spin_lock_bh(&phy->amnt_lock); |
| 175 | + for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++) { |
| 176 | + struct mt7996_air_monitor_entry *entry; |
| 177 | + |
| 178 | + if (ether_addr_equal(addr, ctrl->entry[i].addr)) { |
| 179 | + entry = &ctrl->entry[i]; |
| 180 | + entry->rssi[0] = status->chain_signal[0]; |
| 181 | + entry->rssi[1] = status->chain_signal[1]; |
| 182 | + entry->rssi[2] = status->chain_signal[2]; |
| 183 | + entry->rssi[3] = status->chain_signal[3]; |
| 184 | + entry->last_seen = jiffies; |
| 185 | + break; |
| 186 | + } |
| 187 | + } |
| 188 | + spin_unlock_bh(&phy->amnt_lock); |
| 189 | +} |
| 190 | + |
| 191 | +static int |
| 192 | +mt7996_vendor_smesh_ctrl(struct mt7996_phy *phy, u8 write, |
| 193 | + u8 enable, u8 *value) |
| 194 | +{ |
| 195 | +#define UNI_CMD_SMESH_PARAM 0 |
| 196 | + struct mt7996_dev *dev = phy->dev; |
| 197 | + struct smesh_param { |
| 198 | + __le16 tag; |
| 199 | + __le16 length; |
| 200 | + |
| 201 | + u8 enable; |
| 202 | + bool a2; |
| 203 | + bool a1; |
| 204 | + bool data; |
| 205 | + bool mgnt; |
| 206 | + bool ctrl; |
| 207 | + u8 padding[2]; |
| 208 | + } req = { |
| 209 | + .tag = cpu_to_le16(UNI_CMD_SMESH_PARAM), |
| 210 | + .length = cpu_to_le16(sizeof(req) - 4), |
| 211 | + |
| 212 | + .enable = enable, |
| 213 | + .a2 = true, |
| 214 | + .a1 = true, |
| 215 | + .data = true, |
| 216 | + .mgnt = false, |
| 217 | + .ctrl = false, |
| 218 | + }; |
| 219 | + struct smesh_param *res; |
| 220 | + struct sk_buff *skb; |
| 221 | + int ret = 0; |
| 222 | + |
| 223 | + if (!value) |
| 224 | + return -EINVAL; |
| 225 | + |
| 226 | + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD(CFG_SMESH), |
| 227 | + &req, sizeof(req), !write, &skb); |
| 228 | + |
| 229 | + if (ret || write) |
| 230 | + return ret; |
| 231 | + |
| 232 | + res = (struct smesh_param *) skb->data; |
| 233 | + |
| 234 | + *value = res->enable; |
| 235 | + |
| 236 | + dev_kfree_skb(skb); |
| 237 | + |
| 238 | + return 0; |
| 239 | +} |
| 240 | + |
| 241 | +static int |
| 242 | +mt7996_vendor_amnt_muar(struct mt7996_phy *phy, u8 muar_idx, u8 *addr) |
| 243 | +{ |
| 244 | +#define UNI_CMD_MUAR_ENTRY 2 |
| 245 | + struct mt7996_dev *dev = phy->dev; |
| 246 | + struct muar_entry { |
| 247 | + __le16 tag; |
| 248 | + __le16 length; |
| 249 | + |
| 250 | + bool smesh; |
| 251 | + u8 hw_bss_index; |
| 252 | + u8 muar_idx; |
| 253 | + u8 entry_add; |
| 254 | + u8 mac_addr[6]; |
| 255 | + u8 padding[2]; |
| 256 | + } __packed req = { |
| 257 | + .tag = cpu_to_le16(UNI_CMD_MUAR_ENTRY), |
| 258 | + .length = cpu_to_le16(sizeof(req) - 4), |
| 259 | + |
| 260 | + .smesh = true, |
| 261 | + .hw_bss_index = phy != &dev->phy, |
| 262 | + .muar_idx = muar_idx, |
| 263 | + .entry_add = 1, |
| 264 | + }; |
| 265 | + |
| 266 | + ether_addr_copy(req.mac_addr, addr); |
| 267 | + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REPT_MUAR), &req, |
| 268 | + sizeof(req), true); |
| 269 | +} |
| 270 | + |
| 271 | +static int |
| 272 | +mt7996_vendor_amnt_set_en(struct mt7996_phy *phy, u8 enable) |
| 273 | +{ |
| 274 | + u8 status; |
| 275 | + int ret; |
| 276 | + |
| 277 | + ret = mt7996_vendor_smesh_ctrl(phy, 0, enable, &status); |
| 278 | + if (ret) |
| 279 | + return ret; |
| 280 | + |
| 281 | + if (status == enable) |
| 282 | + return 0; |
| 283 | + |
| 284 | + ret = mt7996_vendor_smesh_ctrl(phy, 1, enable, &status); |
| 285 | + if (ret) |
| 286 | + return ret; |
| 287 | + |
| 288 | + return 0; |
| 289 | +} |
| 290 | + |
| 291 | +static int |
| 292 | +mt7996_vendor_amnt_set_addr(struct mt7996_phy *phy, u8 index, u8 *addr) |
| 293 | +{ |
| 294 | + struct mt7996_air_monitor_ctrl *amnt_ctrl = &phy->amnt_ctrl; |
| 295 | + struct mt7996_air_monitor_group *group; |
| 296 | + struct mt7996_air_monitor_entry *entry; |
| 297 | + int ret, i, j; |
| 298 | + |
| 299 | + if (index >= MT7996_AIR_MONITOR_MAX_ENTRY) |
| 300 | + return -1; |
| 301 | + |
| 302 | + spin_lock_bh(&phy->amnt_lock); |
| 303 | + entry = &amnt_ctrl->entry[index]; |
| 304 | + if (!is_zero_ether_addr(addr)) { |
| 305 | + if (entry->enable == false) { |
| 306 | + for (i = 0; i < MT7996_AIR_MONITOR_MAX_GROUP; i++) { |
| 307 | + group = &(amnt_ctrl->group[i]); |
| 308 | + if (group->used[0] == false) |
| 309 | + j = 0; |
| 310 | + else if (group->used[1] == false) |
| 311 | + j = 1; |
| 312 | + else |
| 313 | + continue; |
| 314 | + |
| 315 | + group->enable = true; |
| 316 | + group->used[j] = true; |
| 317 | + entry->enable = true; |
| 318 | + entry->group_idx = i; |
| 319 | + entry->group_used_idx = j; |
| 320 | + entry->muar_idx = 32 + 4 * i + 2 * j; |
| 321 | + break; |
| 322 | + } |
| 323 | + } |
| 324 | + } else { |
| 325 | + group = &(amnt_ctrl->group[entry->group_idx]); |
| 326 | + |
| 327 | + group->used[entry->group_used_idx] = false; |
| 328 | + if (group->used[0] == false && group->used[1] == false) |
| 329 | + group->enable = false; |
| 330 | + |
| 331 | + entry->enable = false; |
| 332 | + } |
| 333 | + ether_addr_copy(entry->addr, addr); |
| 334 | + amnt_ctrl->enable &= ~(1 << entry->group_idx); |
| 335 | + amnt_ctrl->enable |= entry->enable << entry->group_idx; |
| 336 | + spin_unlock_bh(&phy->amnt_lock); |
| 337 | + |
| 338 | + ret = mt7996_vendor_amnt_muar(phy, entry->muar_idx, addr); |
| 339 | + if (ret) |
| 340 | + return ret; |
| 341 | + |
| 342 | + return mt7996_vendor_amnt_set_en(phy, amnt_ctrl->enable); |
| 343 | +} |
| 344 | + |
| 345 | +static int |
| 346 | +mt7966_vendor_amnt_ctrl(struct wiphy *wiphy, struct wireless_dev *wdev, |
| 347 | + const void *data, int data_len) |
| 348 | +{ |
| 349 | + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); |
| 350 | + struct mt7996_phy *phy = mt7996_hw_phy(hw); |
| 351 | + struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL]; |
| 352 | + struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_SET]; |
| 353 | + u8 index = 0; |
| 354 | + u8 mac_addr[ETH_ALEN]; |
| 355 | + int err; |
| 356 | + |
| 357 | + err = nla_parse(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX, data, data_len, |
| 358 | + amnt_ctrl_policy, NULL); |
| 359 | + if (err) |
| 360 | + return err; |
| 361 | + |
| 362 | + if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_SET]) |
| 363 | + return -EINVAL; |
| 364 | + |
| 365 | + err = nla_parse_nested(tb2, MTK_VENDOR_ATTR_AMNT_SET_MAX, |
| 366 | + tb1[MTK_VENDOR_ATTR_AMNT_CTRL_SET], amnt_set_policy, NULL); |
| 367 | + |
| 368 | + if (!tb2[MTK_VENDOR_ATTR_AMNT_SET_INDEX] || |
| 369 | + !tb2[MTK_VENDOR_ATTR_AMNT_SET_MACADDR]) |
| 370 | + return -EINVAL; |
| 371 | + |
| 372 | + index = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_SET_INDEX]); |
| 373 | + memcpy(mac_addr, nla_data(tb2[MTK_VENDOR_ATTR_AMNT_SET_MACADDR]), ETH_ALEN); |
| 374 | + |
| 375 | + return mt7996_vendor_amnt_set_addr(phy, index, mac_addr); |
| 376 | +} |
| 377 | + |
| 378 | +int mt7996_vendor_amnt_sta_remove(struct mt7996_phy *phy, |
| 379 | + struct ieee80211_sta *sta) |
| 380 | +{ |
| 381 | + u8 zero[ETH_ALEN] = {}; |
| 382 | + int i; |
| 383 | + |
| 384 | + if (!phy->amnt_ctrl.enable) |
| 385 | + return 0; |
| 386 | + |
| 387 | + for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++) |
| 388 | + if (ether_addr_equal(sta->addr, phy->amnt_ctrl.entry[i].addr)) |
| 389 | + return mt7996_vendor_amnt_set_addr(phy, i, zero); |
| 390 | + return 0; |
| 391 | +} |
| 392 | + |
| 393 | +static int |
| 394 | +mt7996_amnt_dump(struct mt7996_phy *phy, struct sk_buff *skb, |
| 395 | + u8 amnt_idx, int *attrtype) |
| 396 | +{ |
| 397 | + struct mt7996_air_monitor_entry *entry; |
| 398 | + struct mt7996_amnt_data data; |
| 399 | + u32 last_seen = 0; |
| 400 | + |
| 401 | + spin_lock_bh(&phy->amnt_lock); |
| 402 | + entry = &phy->amnt_ctrl.entry[amnt_idx]; |
| 403 | + if (entry->enable == 0) { |
| 404 | + spin_unlock_bh(&phy->amnt_lock); |
| 405 | + return 0; |
| 406 | + } |
| 407 | + |
| 408 | + last_seen = jiffies_to_msecs(jiffies - entry->last_seen); |
| 409 | + ether_addr_copy(data.addr, entry->addr); |
| 410 | + data.rssi[0] = entry->rssi[0]; |
| 411 | + data.rssi[1] = entry->rssi[1]; |
| 412 | + data.rssi[2] = entry->rssi[2]; |
| 413 | + data.rssi[3] = entry->rssi[3]; |
| 414 | + spin_unlock_bh(&phy->amnt_lock); |
| 415 | + |
| 416 | + data.idx = amnt_idx; |
| 417 | + data.last_seen = last_seen; |
| 418 | + |
| 419 | + nla_put(skb, (*attrtype)++, sizeof(struct mt7996_amnt_data), &data); |
| 420 | + |
| 421 | + return 1; |
| 422 | +} |
| 423 | + |
| 424 | +static int |
| 425 | +mt7966_vendor_amnt_ctrl_dump(struct wiphy *wiphy, struct wireless_dev *wdev, |
| 426 | + struct sk_buff *skb, const void *data, int data_len, |
| 427 | + unsigned long *storage) |
| 428 | +{ |
| 429 | + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); |
| 430 | + struct mt7996_phy *phy = mt7996_hw_phy(hw); |
| 431 | + struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_AMNT_CTRL]; |
| 432 | + struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_AMNT_DUMP]; |
| 433 | + void *a, *b; |
| 434 | + int err = 0, attrtype = 0, i, len = 0; |
| 435 | + u8 amnt_idx; |
| 436 | + |
| 437 | + if (*storage == 1) |
| 438 | + return -ENOENT; |
| 439 | + *storage = 1; |
| 440 | + |
| 441 | + err = nla_parse(tb1, MTK_VENDOR_ATTR_AMNT_CTRL_MAX, data, data_len, |
| 442 | + amnt_ctrl_policy, NULL); |
| 443 | + if (err) |
| 444 | + return err; |
| 445 | + |
| 446 | + if (!tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP]) |
| 447 | + return -EINVAL; |
| 448 | + |
| 449 | + err = nla_parse_nested(tb2, MTK_VENDOR_ATTR_AMNT_DUMP_MAX, |
| 450 | + tb1[MTK_VENDOR_ATTR_AMNT_CTRL_DUMP], |
| 451 | + amnt_dump_policy, NULL); |
| 452 | + if (err) |
| 453 | + return err; |
| 454 | + |
| 455 | + if (!tb2[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX]) |
| 456 | + return -EINVAL; |
| 457 | + |
| 458 | + amnt_idx = nla_get_u8(tb2[MTK_VENDOR_ATTR_AMNT_DUMP_INDEX]); |
| 459 | + |
| 460 | + a = nla_nest_start(skb, MTK_VENDOR_ATTR_AMNT_CTRL_DUMP); |
| 461 | + b = nla_nest_start(skb, MTK_VENDOR_ATTR_AMNT_DUMP_RESULT); |
| 462 | + |
| 463 | + if (amnt_idx != 0xff) { |
| 464 | + len += mt7996_amnt_dump(phy, skb, amnt_idx, &attrtype); |
| 465 | + } else { |
| 466 | + for (i = 0; i < MT7996_AIR_MONITOR_MAX_ENTRY; i++) |
| 467 | + len += mt7996_amnt_dump(phy, skb, i, &attrtype); |
| 468 | + } |
| 469 | + |
| 470 | + nla_nest_end(skb, b); |
| 471 | + |
| 472 | + nla_put_u8(skb, MTK_VENDOR_ATTR_AMNT_DUMP_LEN, len); |
| 473 | + |
| 474 | + nla_nest_end(skb, a); |
| 475 | + |
| 476 | + return len + 1; |
| 477 | +} |
| 478 | + |
| 479 | + |
| 480 | static const struct wiphy_vendor_command mt7996_vendor_commands[] = { |
| 481 | { |
| 482 | .info = { |
| 483 | @@ -75,10 +423,24 @@ static const struct wiphy_vendor_command mt7996_vendor_commands[] = { |
| 484 | .policy = mu_ctrl_policy, |
| 485 | .maxattr = MTK_VENDOR_ATTR_MU_CTRL_MAX, |
| 486 | }, |
| 487 | + { |
| 488 | + .info = { |
| 489 | + .vendor_id = MTK_NL80211_VENDOR_ID, |
| 490 | + .subcmd = MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL, |
| 491 | + }, |
| 492 | + .flags = WIPHY_VENDOR_CMD_NEED_NETDEV | |
| 493 | + WIPHY_VENDOR_CMD_NEED_RUNNING, |
| 494 | + .doit = mt7966_vendor_amnt_ctrl, |
| 495 | + .dumpit = mt7966_vendor_amnt_ctrl_dump, |
| 496 | + .policy = amnt_ctrl_policy, |
| 497 | + .maxattr = MTK_VENDOR_ATTR_AMNT_CTRL_MAX, |
| 498 | + }, |
| 499 | }; |
| 500 | |
| 501 | void mt7996_vendor_register(struct mt7996_phy *phy) |
| 502 | { |
| 503 | phy->mt76->hw->wiphy->vendor_commands = mt7996_vendor_commands; |
| 504 | phy->mt76->hw->wiphy->n_vendor_commands = ARRAY_SIZE(mt7996_vendor_commands); |
| 505 | + |
| 506 | + spin_lock_init(&phy->amnt_lock); |
| 507 | } |
| 508 | diff --git a/mt7996/vendor.h b/mt7996/vendor.h |
| 509 | index 8ac3ba8e..2078cafa 100644 |
| 510 | --- a/mt7996/vendor.h |
| 511 | +++ b/mt7996/vendor.h |
| 512 | @@ -4,6 +4,7 @@ |
| 513 | #define MTK_NL80211_VENDOR_ID 0x0ce7 |
| 514 | |
| 515 | enum mtk_nl80211_vendor_subcmds { |
| 516 | + MTK_NL80211_VENDOR_SUBCMD_AMNT_CTRL = 0xae, |
| 517 | MTK_NL80211_VENDOR_SUBCMD_MU_CTRL = 0xc5, |
| 518 | }; |
| 519 | |
| 520 | @@ -19,4 +20,42 @@ enum mtk_vendor_attr_mu_ctrl { |
| 521 | NUM_MTK_VENDOR_ATTRS_MU_CTRL - 1 |
| 522 | }; |
| 523 | |
| 524 | +enum mtk_vendor_attr_mnt_ctrl { |
| 525 | + MTK_VENDOR_ATTR_AMNT_CTRL_UNSPEC, |
| 526 | + |
| 527 | + MTK_VENDOR_ATTR_AMNT_CTRL_SET, |
| 528 | + MTK_VENDOR_ATTR_AMNT_CTRL_DUMP, |
| 529 | + |
| 530 | + /* keep last */ |
| 531 | + NUM_MTK_VENDOR_ATTRS_AMNT_CTRL, |
| 532 | + MTK_VENDOR_ATTR_AMNT_CTRL_MAX = |
| 533 | + NUM_MTK_VENDOR_ATTRS_AMNT_CTRL - 1 |
| 534 | +}; |
| 535 | + |
| 536 | +enum mtk_vendor_attr_mnt_set { |
| 537 | + MTK_VENDOR_ATTR_AMNT_SET_UNSPEC, |
| 538 | + |
| 539 | + MTK_VENDOR_ATTR_AMNT_SET_INDEX, |
| 540 | + MTK_VENDOR_ATTR_AMNT_SET_MACADDR, |
| 541 | + |
| 542 | + /* keep last */ |
| 543 | + NUM_MTK_VENDOR_ATTRS_AMNT_SET, |
| 544 | + MTK_VENDOR_ATTR_AMNT_SET_MAX = |
| 545 | + NUM_MTK_VENDOR_ATTRS_AMNT_SET - 1 |
| 546 | +}; |
| 547 | + |
| 548 | +enum mtk_vendor_attr_mnt_dump { |
| 549 | + MTK_VENDOR_ATTR_AMNT_DUMP_UNSPEC, |
| 550 | + |
| 551 | + MTK_VENDOR_ATTR_AMNT_DUMP_INDEX, |
| 552 | + MTK_VENDOR_ATTR_AMNT_DUMP_LEN, |
| 553 | + MTK_VENDOR_ATTR_AMNT_DUMP_RESULT, |
| 554 | + |
| 555 | + /* keep last */ |
| 556 | + NUM_MTK_VENDOR_ATTRS_AMNT_DUMP, |
| 557 | + MTK_VENDOR_ATTR_AMNT_DUMP_MAX = |
| 558 | + NUM_MTK_VENDOR_ATTRS_AMNT_DUMP - 1 |
| 559 | +}; |
| 560 | + |
| 561 | + |
| 562 | #endif |
| 563 | -- |
| 564 | 2.39.2 |
| 565 | |