| From b0f18aac90c7144db84f880aedb5aeaba3f37305 Mon Sep 17 00:00:00 2001 |
| From: mtk20656 <chank.chen@mediatek.com> |
| Date: Tue, 6 Feb 2024 15:46:05 +0800 |
| Subject: [PATCH 076/126] mtk: hostapd: add connac3 csi control interface |
| |
| 1. add hostapd_cli interface |
| 2. add csi set/dump flow |
| 3. add csi raw data to json |
| |
| Signed-off-by: mtk20656 <chank.chen@mediatek.com> |
| --- |
| hostapd/ctrl_iface.c | 193 ++++++++++++++++++++++++ |
| hostapd/hostapd_cli.c | 16 ++ |
| src/ap/ap_drv_ops.c | 13 ++ |
| src/ap/ap_drv_ops.h | 2 + |
| src/common/mtk_vendor.h | 33 ++++- |
| src/drivers/driver.h | 18 +++ |
| src/drivers/driver_nl80211.c | 239 ++++++++++++++++++++++++++++++ |
| src/drivers/driver_nl80211.h | 1 + |
| src/drivers/driver_nl80211_capa.c | 3 + |
| 9 files changed, 511 insertions(+), 7 deletions(-) |
| |
| diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c |
| index 76fd25d8b..9bd8a46b3 100644 |
| --- a/hostapd/ctrl_iface.c |
| +++ b/hostapd/ctrl_iface.c |
| @@ -5063,6 +5063,193 @@ hostapd_ctrl_iface_disable_beacon(struct hostapd_data *hapd, char *value, |
| |
| } |
| |
| +static int |
| +hostapd_ctrl_iface_set_csi(struct hostapd_data *hapd, char *cmd, |
| + char *buf, size_t buflen) |
| +{ |
| + char *tmp; |
| + u8 sta_mac[ETH_ALEN] = {0}; |
| + u32 csi_para[4] = {0}; |
| + char mac_str[18] = {0}; |
| + u8 csi_para_cnt = 0; |
| + |
| + tmp = strtok_r(cmd, ",", &cmd); |
| + |
| + while (tmp) { |
| + csi_para_cnt++; |
| + |
| + if (csi_para_cnt <= 4) |
| + csi_para[csi_para_cnt - 1] = strtol(tmp, &tmp, 10); |
| + else if (csi_para_cnt == 5) { |
| + memcpy(mac_str, tmp, sizeof(mac_str) - 1); |
| + break; |
| + } |
| + |
| + tmp = strtok_r(NULL, ",", &cmd); |
| + } |
| + |
| + if (strlen(mac_str)) { /* user input mac string */ |
| + if (hwaddr_aton(mac_str, sta_mac) < 0) { |
| + wpa_printf(MSG_ERROR, "station mac is not right.\n"); |
| + return -1; |
| + } |
| + |
| + if (hostapd_drv_csi_set(hapd, csi_para[0], csi_para[1], csi_para[2], csi_para[3], sta_mac)) { |
| + wpa_printf(MSG_ERROR, "Not able to set csi, %d,%d,%d,%d,%s\n", |
| + csi_para[0], csi_para[1], csi_para[2], csi_para[3], mac_str); |
| + return -1; |
| + } |
| + } else { |
| + if (hostapd_drv_csi_set(hapd, csi_para[0], csi_para[1], csi_para[2], csi_para[3], NULL)) { |
| + wpa_printf(MSG_ERROR, "Not able to set csi, %d,%d,%d,%d\n", |
| + csi_para[0], csi_para[1], csi_para[2], csi_para[3]); |
| + return -1; |
| + } |
| + } |
| + |
| + return os_snprintf(buf, buflen, "OK\n"); |
| +} |
| + |
| +static int mt76_csi_to_json(char *fname, struct csi_resp_data *resp_buf) |
| +{ |
| +#define MAX_BUF_SIZE 10000 |
| + FILE *f; |
| + int i; |
| + |
| + if (!fname) { |
| + wpa_printf(MSG_ERROR, "csi dump file name is null!\n"); |
| + return -1; |
| + } |
| + |
| + f = fopen(fname, "a+"); |
| + if (!f) { |
| + wpa_printf(MSG_ERROR, "open csi dump file %s failed\n", fname); |
| + return -1; |
| + } |
| + |
| + if (fwrite("[", 1, 1, f) != 1) { |
| + fclose(f); |
| + return -1; |
| + } |
| + |
| + for (i = 0; i < resp_buf->buf_cnt; i++) { |
| + struct csi_data *c = &resp_buf->csi_buf[i]; |
| + char *pos, *buf; |
| + int j; |
| + |
| + buf = malloc(MAX_BUF_SIZE); |
| + if (!buf) { |
| + fclose(f); |
| + return -1; |
| + } |
| + |
| + pos = buf; |
| + pos += snprintf(pos, MAX_BUF_SIZE, "%c", '['); |
| + |
| + pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->ts); |
| + pos += snprintf(pos, MAX_BUF_SIZE, "\"%02x%02x%02x%02x%02x%02x\",", c->ta[0], c->ta[1], c->ta[2], c->ta[3], c->ta[4], c->ta[5]); |
| + |
| + pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->rssi); |
| + pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->snr); |
| + pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->data_bw); |
| + pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->pri_ch_idx); |
| + pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->rx_mode); |
| + pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->tx_idx); |
| + pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->rx_idx); |
| + pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->chain_info); |
| + pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->ext_info); |
| + |
| + pos += snprintf(pos, MAX_BUF_SIZE, "%c", '['); |
| + for (j = 0; j < c->data_num; j++) { |
| + pos += snprintf(pos, MAX_BUF_SIZE, "%d", c->data_i[j]); |
| + if (j != (c->data_num - 1)) |
| + pos += snprintf(pos, MAX_BUF_SIZE, ","); |
| + } |
| + pos += snprintf(pos, MAX_BUF_SIZE, "%c,", ']'); |
| + |
| + pos += snprintf(pos, MAX_BUF_SIZE, "%c", '['); |
| + for (j = 0; j < c->data_num; j++) { |
| + pos += snprintf(pos, MAX_BUF_SIZE, "%d", c->data_q[j]); |
| + if (j != (c->data_num - 1)) |
| + pos += snprintf(pos, MAX_BUF_SIZE, ","); |
| + } |
| + pos += snprintf(pos, MAX_BUF_SIZE, "%c", ']'); |
| + |
| + pos += snprintf(pos, MAX_BUF_SIZE, "%c", ']'); |
| + if (i != resp_buf->buf_cnt - 1) |
| + pos += snprintf(pos, MAX_BUF_SIZE, ","); |
| + |
| + if (fwrite(buf, 1, pos - buf, f) != (pos - buf)) { |
| + perror("fwrite"); |
| + free(buf); |
| + fclose(f); |
| + return -1; |
| + } |
| + |
| + free(buf); |
| + } |
| + |
| + if (fwrite("]", 1, 1, f) != 1) { |
| + fclose(f); |
| + return -1; |
| + } |
| + |
| + fclose(f); |
| + |
| + return 0; |
| +} |
| + |
| +static int |
| +hostapd_ctrl_iface_dump_csi(struct hostapd_data *hapd, char *cmd, |
| + char *buf, size_t buflen) |
| +{ |
| + char *tmp, *fname; |
| + int data_cnt = 0, ret = 0; |
| + struct csi_resp_data resp_buf; |
| + |
| + tmp = strtok_r(cmd, ",", &cmd); |
| + |
| + if (!tmp) { |
| + wpa_printf(MSG_ERROR, "Error in command format\n"); |
| + return -1; |
| + } |
| + |
| + data_cnt = strtoul(tmp, &tmp, 0); |
| + |
| + if (data_cnt > 3000) { |
| + wpa_printf(MSG_ERROR, "Wrong input csi data cnt\n"); |
| + return -1; |
| + } |
| + |
| + fname = strtok_r(NULL, ",", &cmd); |
| + |
| + if (!fname) { |
| + wpa_printf(MSG_ERROR, "Error in command format, csi_filename.\n"); |
| + return -1; |
| + } |
| + |
| + resp_buf.csi_buf = (struct csi_data *)os_zalloc(sizeof(struct csi_data) * data_cnt); |
| + |
| + if (resp_buf.csi_buf == NULL) { |
| + wpa_printf(MSG_ERROR, "Error in memory allocation\n"); |
| + return -1; |
| + } |
| + |
| + resp_buf.usr_need_cnt = data_cnt; |
| + resp_buf.buf_cnt = 0; |
| + |
| + if (hostapd_drv_csi_dump(hapd, (void *)&resp_buf)) { |
| + wpa_printf(MSG_ERROR, "Not able to set csi dump\n"); |
| + os_free(resp_buf.csi_buf); |
| + return -1; |
| + } |
| + |
| + mt76_csi_to_json(fname, &resp_buf); |
| + |
| + os_free(resp_buf.csi_buf); |
| + return 0; |
| +} |
| + |
| static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, |
| char *buf, char *reply, |
| int reply_size, |
| @@ -5724,6 +5911,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, |
| } else if (os_strncmp(buf, "NO_BEACON ", 10) == 0) { |
| reply_len = hostapd_ctrl_iface_disable_beacon(hapd, buf + 10, reply, |
| reply_size); |
| + } else if (os_strncmp(buf, "SET_CSI ", 7) == 0) { |
| + reply_len = hostapd_ctrl_iface_set_csi(hapd, buf + 8, |
| + reply, reply_size); |
| + } else if (os_strncmp(buf, "DUMP_CSI ", 8) == 0) { |
| + reply_len = hostapd_ctrl_iface_dump_csi(hapd, buf + 9, |
| + reply, reply_size); |
| } else { |
| os_memcpy(reply, "UNKNOWN COMMAND\n", 16); |
| reply_len = 16; |
| diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c |
| index a98f76eee..f54fa9997 100644 |
| --- a/hostapd/hostapd_cli.c |
| +++ b/hostapd/hostapd_cli.c |
| @@ -1744,6 +1744,18 @@ static int hostapd_cli_cmd_dump_amnt(struct wpa_ctrl *ctrl, int argc, |
| return hostapd_cli_cmd(ctrl, "DUMP_AMNT", 1, argc, argv); |
| } |
| |
| +static int hostapd_cli_cmd_set_csi(struct wpa_ctrl *ctrl, int argc, |
| + char *argv[]) |
| +{ |
| + return hostapd_cli_cmd(ctrl, "SET_CSI", 1, argc, argv); |
| +} |
| + |
| +static int hostapd_cli_cmd_dump_csi(struct wpa_ctrl *ctrl, int argc, |
| + char *argv[]) |
| +{ |
| + return hostapd_cli_cmd(ctrl, "DUMP_CSI", 1, argc, argv); |
| +} |
| + |
| struct hostapd_cli_cmd { |
| const char *cmd; |
| int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); |
| @@ -1992,6 +2004,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { |
| " = Set Station index and mac to monitor"}, |
| { "dump_amnt", hostapd_cli_cmd_dump_amnt, NULL, |
| " = Dump RSSI of monitoring Station"}, |
| + { "set_csi", hostapd_cli_cmd_set_csi, NULL, |
| + " = Set csi configuaration"}, |
| + { "dump_csi", hostapd_cli_cmd_dump_csi, NULL, |
| + " = Dump csi data to a json file"}, |
| { NULL, NULL, NULL, NULL } |
| }; |
| |
| diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c |
| index c7635cae6..a26a3f45d 100644 |
| --- a/src/ap/ap_drv_ops.c |
| +++ b/src/ap/ap_drv_ops.c |
| @@ -1445,3 +1445,16 @@ int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode) |
| return hapd->driver->beacon_ctrl(hapd->drv_priv, beacon_mode); |
| } |
| |
| +int hostapd_drv_csi_set(struct hostapd_data *hapd, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac) |
| +{ |
| + if (!hapd->driver || !hapd->driver->csi_set) |
| + return 0; |
| + return hapd->driver->csi_set(hapd->drv_priv, hapd->iconf->band_idx, mode, cfg, v1, v2, mac); |
| +} |
| + |
| +int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf) |
| +{ |
| + if (!hapd->driver || !hapd->driver->csi_dump) |
| + return 0; |
| + return hapd->driver->csi_dump(hapd->drv_priv, hapd->iconf->band_idx, dump_buf); |
| +} |
| diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h |
| index 8c783610c..9f2e86a9b 100644 |
| --- a/src/ap/ap_drv_ops.h |
| +++ b/src/ap/ap_drv_ops.h |
| @@ -174,6 +174,8 @@ int hostapd_drv_amnt_dump(struct hostapd_data *hapd, u8 amnt_idx, u8 *amnt_dump_ |
| int hostapd_drv_background_radar_mode(struct hostapd_data *hapd); |
| int hostapd_drv_pp_mode_set(struct hostapd_data *hapd); |
| int hostapd_drv_beacon_ctrl(struct hostapd_data *hapd, u8 beacon_mode); |
| +int hostapd_drv_csi_set(struct hostapd_data *hapd, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac); |
| +int hostapd_drv_csi_dump(struct hostapd_data *hapd, void *dump_buf); |
| |
| #include "drivers/driver.h" |
| |
| diff --git a/src/common/mtk_vendor.h b/src/common/mtk_vendor.h |
| index 9b054ef43..ce3a2ad9a 100644 |
| --- a/src/common/mtk_vendor.h |
| +++ b/src/common/mtk_vendor.h |
| @@ -73,12 +73,13 @@ enum mtk_vendor_attr_csi_ctrl { |
| MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1, |
| MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2, |
| MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR, |
| - MTK_VENDOR_ATTR_CSI_CTRL_INTERVAL, |
| |
| MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM, |
| |
| MTK_VENDOR_ATTR_CSI_CTRL_DATA, |
| |
| + MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX, |
| + |
| /* keep last */ |
| NUM_MTK_VENDOR_ATTRS_CSI_CTRL, |
| MTK_VENDOR_ATTR_CSI_CTRL_MAX = |
| @@ -96,6 +97,7 @@ enum mtk_vendor_attr_csi_data { |
| MTK_VENDOR_ATTR_CSI_DATA_BW, |
| MTK_VENDOR_ATTR_CSI_DATA_CH_IDX, |
| MTK_VENDOR_ATTR_CSI_DATA_TA, |
| + MTK_VENDOR_ATTR_CSI_DATA_NUM, |
| MTK_VENDOR_ATTR_CSI_DATA_I, |
| MTK_VENDOR_ATTR_CSI_DATA_Q, |
| MTK_VENDOR_ATTR_CSI_DATA_INFO, |
| @@ -106,7 +108,7 @@ enum mtk_vendor_attr_csi_data { |
| MTK_VENDOR_ATTR_CSI_DATA_TX_ANT, |
| MTK_VENDOR_ATTR_CSI_DATA_RX_ANT, |
| MTK_VENDOR_ATTR_CSI_DATA_MODE, |
| - MTK_VENDOR_ATTR_CSI_DATA_H_IDX, |
| + MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO, |
| |
| /* keep last */ |
| NUM_MTK_VENDOR_ATTRS_CSI_DATA, |
| @@ -284,23 +286,40 @@ enum mtk_vendor_attr_beacon_ctrl { |
| NUM_MTK_VENDOR_ATTRS_BEACON_CTRL - 1 |
| }; |
| |
| -#define CSI_MAX_COUNT 256 |
| +#define CSI_BW20_DATA_COUNT 64 |
| +#define CSI_BW40_DATA_COUNT 128 |
| +#define CSI_BW80_DATA_COUNT 256 |
| +#define CSI_BW160_DATA_COUNT 512 |
| +#define CSI_BW320_DATA_COUNT 1024 |
| #define ETH_ALEN 6 |
| |
| struct csi_data { |
| - s16 data_i[CSI_MAX_COUNT]; |
| - s16 data_q[CSI_MAX_COUNT]; |
| + u8 ch_bw; |
| + u16 data_num; |
| + s16 data_i[CSI_BW320_DATA_COUNT]; |
| + s16 data_q[CSI_BW320_DATA_COUNT]; |
| + u8 band; |
| s8 rssi; |
| u8 snr; |
| u32 ts; |
| u8 data_bw; |
| u8 pri_ch_idx; |
| u8 ta[ETH_ALEN]; |
| - u32 info; |
| + u32 ext_info; |
| u8 rx_mode; |
| - u32 h_idx; |
| + u32 chain_info; |
| u16 tx_idx; |
| u16 rx_idx; |
| + u32 segment_num; |
| + u8 remain_last; |
| + u16 pkt_sn; |
| + u8 tr_stream; |
| +}; |
| + |
| +struct csi_resp_data { |
| + u16 usr_need_cnt; |
| + u16 buf_cnt; |
| + struct csi_data *csi_buf; |
| }; |
| |
| #define AIR_MONITOR_MAX_ENTRY 16 |
| diff --git a/src/drivers/driver.h b/src/drivers/driver.h |
| index ec00c36e4..48cb93cfd 100644 |
| --- a/src/drivers/driver.h |
| +++ b/src/drivers/driver.h |
| @@ -5369,6 +5369,24 @@ struct wpa_driver_ops { |
| #ifdef CONFIG_IEEE80211BE |
| int (*get_mld_addr)(void *priv, u8 *addr); |
| #endif |
| + /** |
| + * csi_set - Set csi related mode and parameter |
| + * @priv: Private driver interface data |
| + * @band_idx: band idx |
| + * @mode: Csi mode parameter |
| + * @cfg: Csi config parameter |
| + * @v1: Value1 |
| + * @v2: Value2 |
| + * @mac: Station mac for station filter |
| + */ |
| + int (*csi_set)(void *priv, u8 band_idx, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac); |
| + /** |
| + * csi_dump - Dump csi data to json file |
| + * @priv: Private driver interface data |
| + * @band_idx: band idx |
| + * @dump_buf: Dump_struct that store csi data and related info |
| + */ |
| + int (*csi_dump)(void *priv, u8 band_idx, void *dump_buf); |
| }; |
| |
| /** |
| diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c |
| index 8f7215d62..afb068091 100644 |
| --- a/src/drivers/driver_nl80211.c |
| +++ b/src/drivers/driver_nl80211.c |
| @@ -161,6 +161,36 @@ pp_ctrl_policy[NUM_MTK_VENDOR_ATTRS_PP_CTRL] = { |
| [MTK_VENDOR_ATTR_PP_MODE] = { .type = NLA_U8 }, |
| }; |
| |
| +static struct nla_policy csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = { |
| + [MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX] = { .type = NLA_U8 }, |
| + [MTK_VENDOR_ATTR_CSI_CTRL_CFG] = { .type = NLA_NESTED }, |
| + [MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE] = { .type = NLA_U8 }, |
| + [MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE] = { .type = NLA_U8 }, |
| + [MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1] = { .type = NLA_U8 }, |
| + [MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2] = { .type = NLA_U32 }, |
| + [MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR] = { .type = NLA_NESTED }, |
| + [MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM] = { .type = NLA_U16 }, |
| + [MTK_VENDOR_ATTR_CSI_CTRL_DATA] = { .type = NLA_NESTED }, |
| +}; |
| + |
| +static struct nla_policy csi_data_policy[NUM_MTK_VENDOR_ATTRS_CSI_DATA] = { |
| + [MTK_VENDOR_ATTR_CSI_DATA_VER] = { .type = NLA_U8 }, |
| + [MTK_VENDOR_ATTR_CSI_DATA_TS] = { .type = NLA_U32 }, |
| + [MTK_VENDOR_ATTR_CSI_DATA_RSSI] = { .type = NLA_U8 }, |
| + [MTK_VENDOR_ATTR_CSI_DATA_SNR] = { .type = NLA_U8 }, |
| + [MTK_VENDOR_ATTR_CSI_DATA_BW] = { .type = NLA_U8 }, |
| + [MTK_VENDOR_ATTR_CSI_DATA_CH_IDX] = { .type = NLA_U8 }, |
| + [MTK_VENDOR_ATTR_CSI_DATA_TA] = { .type = NLA_NESTED }, |
| + [MTK_VENDOR_ATTR_CSI_DATA_NUM] = { .type = NLA_U32 }, |
| + [MTK_VENDOR_ATTR_CSI_DATA_I] = { .type = NLA_NESTED }, |
| + [MTK_VENDOR_ATTR_CSI_DATA_Q] = { .type = NLA_NESTED }, |
| + [MTK_VENDOR_ATTR_CSI_DATA_INFO] = { .type = NLA_U32 }, |
| + [MTK_VENDOR_ATTR_CSI_DATA_TX_ANT] = { .type = NLA_U8 }, |
| + [MTK_VENDOR_ATTR_CSI_DATA_RX_ANT] = { .type = NLA_U8 }, |
| + [MTK_VENDOR_ATTR_CSI_DATA_MODE] = { .type = NLA_U8 }, |
| + [MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO] = { .type = NLA_U32 }, |
| +}; |
| + |
| static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg) |
| { |
| struct nl_sock *handle; |
| @@ -15220,6 +15250,213 @@ static int nl80211_get_mld_addr(void *priv, u8 *addr) |
| } |
| #endif |
| |
| +static int |
| +nl80211_csi_set(void *priv, u8 band_idx, u8 mode, u8 cfg, u8 v1, u32 v2, u8 *mac) |
| +{ |
| + struct i802_bss *bss = priv; |
| + struct wpa_driver_nl80211_data *drv = bss->drv; |
| + struct nl_msg *msg; |
| + struct nlattr *data; |
| + void *tb1, *tb2; |
| + int ret, i; |
| + |
| + if (!drv->mtk_csi_vendor_cmd_avail) { |
| + wpa_printf(MSG_ERROR, |
| + "nl80211: Driver does not support csi"); |
| + return 0; |
| + } |
| + |
| + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR); |
| + if (!msg) |
| + goto fail; |
| + |
| + if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) || |
| + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, |
| + MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL)) |
| + goto fail; |
| + |
| + data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED); |
| + if (!data) |
| + goto fail; |
| + |
| + nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX, band_idx); |
| + |
| + tb1 = nla_nest_start(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG | NLA_F_NESTED); |
| + if (!tb1) |
| + goto fail; |
| + |
| + nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE, mode); |
| + nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE, cfg); |
| + nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1, v1); |
| + nla_put_u32(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2, v2); |
| + |
| + nla_nest_end(msg, tb1); |
| + |
| + if (mac) { |
| + tb2 = nla_nest_start(msg, MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR | NLA_F_NESTED); |
| + if (!tb2) |
| + goto fail; |
| + |
| + for (i = 0; i < ETH_ALEN; i++) |
| + nla_put_u8(msg, i, mac[i]); |
| + |
| + nla_nest_end(msg, tb2); |
| + } |
| + |
| + nla_nest_end(msg, data); |
| + |
| + ret = send_and_recv_cmd(drv, msg); |
| + |
| + if (ret) |
| + wpa_printf(MSG_ERROR, "Failed to set csi. ret=%d (%s)", |
| + ret, strerror(-ret)); |
| + |
| + return ret; |
| + |
| +fail: |
| + nlmsg_free(msg); |
| + return -ENOBUFS; |
| + |
| +} |
| + |
| +static int |
| +mt76_csi_dump_cb(struct nl_msg *msg, void *arg) |
| +{ |
| + struct nlattr *tb[NL80211_ATTR_MAX + 1]; |
| + struct nlattr *tb1[NUM_MTK_VENDOR_ATTRS_CSI_CTRL]; |
| + struct nlattr *tb2[NUM_MTK_VENDOR_ATTRS_CSI_DATA]; |
| + struct nlattr *attr, *cur, *data; |
| + int len = 0, rem, idx; |
| + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); |
| + struct csi_resp_data *csi_resp = (struct csi_resp_data *)arg; |
| + struct csi_data *c = csi_resp->csi_buf; |
| + |
| + c += csi_resp->buf_cnt; |
| + |
| + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), |
| + genlmsg_attrlen(gnlh, 0), NULL); |
| + |
| + attr = tb[NL80211_ATTR_VENDOR_DATA]; |
| + if (!attr) |
| + return NL_SKIP; |
| + |
| + nla_parse_nested(tb1, MTK_VENDOR_ATTR_CSI_CTRL_MAX, |
| + attr, csi_ctrl_policy); |
| + |
| + if (!tb1[MTK_VENDOR_ATTR_CSI_CTRL_DATA]) |
| + return NL_SKIP; |
| + |
| + nla_parse_nested(tb2, MTK_VENDOR_ATTR_CSI_DATA_MAX, |
| + tb1[MTK_VENDOR_ATTR_CSI_CTRL_DATA], csi_data_policy); |
| + |
| + if (!(tb2[MTK_VENDOR_ATTR_CSI_DATA_VER] && |
| + tb2[MTK_VENDOR_ATTR_CSI_DATA_TS] && |
| + tb2[MTK_VENDOR_ATTR_CSI_DATA_RSSI] && |
| + tb2[MTK_VENDOR_ATTR_CSI_DATA_SNR] && |
| + tb2[MTK_VENDOR_ATTR_CSI_DATA_BW] && |
| + tb2[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX] && |
| + tb2[MTK_VENDOR_ATTR_CSI_DATA_TA] && |
| + tb2[MTK_VENDOR_ATTR_CSI_DATA_I] && |
| + tb2[MTK_VENDOR_ATTR_CSI_DATA_Q] && |
| + tb2[MTK_VENDOR_ATTR_CSI_DATA_INFO] && |
| + tb2[MTK_VENDOR_ATTR_CSI_DATA_MODE] && |
| + tb2[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO])) { |
| + fprintf(stderr, "Attributes error for CSI data\n"); |
| + return NL_SKIP; |
| + } |
| + |
| + c->rssi = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_RSSI]); |
| + c->snr = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_SNR]); |
| + c->data_bw = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_BW]); |
| + c->pri_ch_idx = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX]); |
| + c->rx_mode = nla_get_u8(tb2[MTK_VENDOR_ATTR_CSI_DATA_MODE]); |
| + |
| + c->tx_idx = nla_get_u16(tb2[MTK_VENDOR_ATTR_CSI_DATA_TX_ANT]); |
| + c->rx_idx = nla_get_u16(tb2[MTK_VENDOR_ATTR_CSI_DATA_RX_ANT]); |
| + |
| + c->ext_info = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_INFO]); |
| + c->chain_info = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_CHAIN_INFO]); |
| + |
| + c->ts = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_TS]); |
| + |
| + c->data_num = nla_get_u32(tb2[MTK_VENDOR_ATTR_CSI_DATA_NUM]); |
| + |
| + idx = 0; |
| + nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_TA], rem) { |
| + if (idx < ETH_ALEN) |
| + c->ta[idx++] = nla_get_u8(cur); |
| + } |
| + |
| + idx = 0; |
| + nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_I], rem) { |
| + if (idx < c->data_num) |
| + c->data_i[idx++] = nla_get_u16(cur); |
| + } |
| + |
| + idx = 0; |
| + nla_for_each_nested(cur, tb2[MTK_VENDOR_ATTR_CSI_DATA_Q], rem) { |
| + if (idx < c->data_num) |
| + c->data_q[idx++] = nla_get_u16(cur); |
| + } |
| + |
| + csi_resp->buf_cnt++; |
| + |
| + return NL_SKIP; |
| +} |
| + |
| +static int |
| +nl80211_csi_dump(void *priv, u8 band_idx, void *dump_buf) |
| +{ |
| + struct i802_bss *bss = priv; |
| + struct wpa_driver_nl80211_data *drv = bss->drv; |
| + struct nl_msg *msg; |
| + struct nlattr *data; |
| + int ret; |
| + struct csi_resp_data *csi_resp; |
| + u16 pkt_num, i; |
| + |
| + if (!drv->mtk_csi_vendor_cmd_avail) { |
| + wpa_printf(MSG_INFO, |
| + "nl80211: Driver does not support csi"); |
| + return 0; |
| + } |
| + |
| + csi_resp = (struct csi_resp_data *)dump_buf; |
| + pkt_num = csi_resp->usr_need_cnt; |
| + |
| + if (pkt_num > 3000) |
| + return -EINVAL; |
| + |
| +#define CSI_DUMP_PER_NUM 3 |
| + for (i = 0; i < pkt_num / CSI_DUMP_PER_NUM; i++) { |
| + msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_VENDOR); |
| + if (!msg) |
| + goto fail; |
| + |
| + if (nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_MTK) || |
| + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, |
| + MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL)) |
| + goto fail; |
| + |
| + data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED); |
| + if (!data) |
| + goto fail; |
| + |
| + nla_put_u8(msg, MTK_VENDOR_ATTR_CSI_CTRL_BAND_IDX, band_idx); |
| + nla_put_u16(msg, MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM, CSI_DUMP_PER_NUM); |
| + |
| + nla_nest_end(msg, data); |
| + |
| + ret = send_and_recv_resp(drv, msg, mt76_csi_dump_cb, dump_buf); |
| + } |
| + |
| + return ret; |
| + |
| +fail: |
| + nlmsg_free(msg); |
| + return -ENOBUFS; |
| +} |
| + |
| const struct wpa_driver_ops wpa_driver_nl80211_ops = { |
| .name = "nl80211", |
| .desc = "Linux nl80211/cfg80211", |
| @@ -15403,4 +15640,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { |
| #ifdef CONFIG_IEEE80211BE |
| .get_mld_addr = nl80211_get_mld_addr, |
| #endif |
| + .csi_set = nl80211_csi_set, |
| + .csi_dump = nl80211_csi_dump, |
| }; |
| diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h |
| index a0a62e536..b710b50cd 100644 |
| --- a/src/drivers/driver_nl80211.h |
| +++ b/src/drivers/driver_nl80211.h |
| @@ -212,6 +212,7 @@ struct wpa_driver_nl80211_data { |
| unsigned int mtk_background_radar_vendor_cmd_avail:1; |
| unsigned int mtk_pp_vendor_cmd_avail:1; |
| unsigned int mtk_beacon_ctrl_vendor_cmd_avail:1; |
| + unsigned int mtk_csi_vendor_cmd_avail:1; |
| |
| u32 ignore_next_local_disconnect; |
| u32 ignore_next_local_deauth; |
| diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c |
| index 30f89b687..5a53700d1 100644 |
| --- a/src/drivers/driver_nl80211_capa.c |
| +++ b/src/drivers/driver_nl80211_capa.c |
| @@ -1173,6 +1173,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) |
| case MTK_NL80211_VENDOR_SUBCMD_BEACON_CTRL : |
| drv->mtk_beacon_ctrl_vendor_cmd_avail = 1; |
| break; |
| + case MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL: |
| + drv->mtk_csi_vendor_cmd_avail = 1; |
| + break; |
| } |
| } |
| |
| -- |
| 2.18.0 |
| |