blob: 188a1514fea3493e67d0eb99db3875bcc0761078 [file] [log] [blame]
developerfd40db22021-04-29 10:08:25 +08001// SPDX-License-Identifier: ISC
2/* Copyright (C) 2021 Mediatek Inc. */
3#define _GNU_SOURCE
4
5#include <errno.h>
6#include <net/if.h>
7
8#include "mt76-vendor.h"
9
10struct unl unl;
11static const char *progname;
12struct csi_data *csi;
13int csi_idx;
14
15static struct nla_policy csi_ctrl_policy[NUM_MTK_VENDOR_ATTRS_CSI_CTRL] = {
16 [MTK_VENDOR_ATTR_CSI_CTRL_CFG] = { .type = NLA_NESTED },
17 [MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE] = { .type = NLA_U8 },
18 [MTK_VENDOR_ATTR_CSI_CTRL_CFG_TYPE] = { .type = NLA_U8 },
19 [MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL1] = { .type = NLA_U8 },
20 [MTK_VENDOR_ATTR_CSI_CTRL_CFG_VAL2] = { .type = NLA_U8 },
21 [MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR] = { .type = NLA_NESTED },
22 [MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM] = { .type = NLA_U16 },
23 [MTK_VENDOR_ATTR_CSI_CTRL_DATA] = { .type = NLA_NESTED },
24};
25
26static struct nla_policy csi_data_policy[NUM_MTK_VENDOR_ATTRS_CSI_DATA] = {
27 [MTK_VENDOR_ATTR_CSI_DATA_VER] = { .type = NLA_U8 },
28 [MTK_VENDOR_ATTR_CSI_DATA_TS] = { .type = NLA_U64 },
29 [MTK_VENDOR_ATTR_CSI_DATA_RSSI] = { .type = NLA_U8 },
30 [MTK_VENDOR_ATTR_CSI_DATA_SNR] = { .type = NLA_U8 },
31 [MTK_VENDOR_ATTR_CSI_DATA_BW] = { .type = NLA_U8 },
32 [MTK_VENDOR_ATTR_CSI_DATA_CH_IDX] = { .type = NLA_U8 },
33 [MTK_VENDOR_ATTR_CSI_DATA_TA] = { .type = NLA_NESTED },
34 [MTK_VENDOR_ATTR_CSI_DATA_I] = { .type = NLA_NESTED },
35 [MTK_VENDOR_ATTR_CSI_DATA_Q] = { .type = NLA_NESTED },
36 [MTK_VENDOR_ATTR_CSI_DATA_INFO] = { .type = NLA_U32 },
37 [MTK_VENDOR_ATTR_CSI_DATA_TX_ANT] = { .type = NLA_U8 },
38 [MTK_VENDOR_ATTR_CSI_DATA_RX_ANT] = { .type = NLA_U8 },
39 [MTK_VENDOR_ATTR_CSI_DATA_MODE] = { .type = NLA_U8 },
40 [MTK_VENDOR_ATTR_CSI_DATA_H_IDX] = { .type = NLA_U32 },
41};
42
43void usage(void)
44{
45 static const char *const commands[] = {
46 "set csi_ctrl=",
47 "dump <packet num> <filename>",
48 };
49 int i;
50
51 fprintf(stderr, "Usage:\n");
52 for (i = 0; i < ARRAY_SIZE(commands); i++)
53 printf(" %s wlanX %s\n", progname, commands[i]);
54
55 exit(1);
56}
57
58static int mt76_dump_cb(struct nl_msg *msg, void *arg)
59{
60 struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_CSI_CTRL];
61 struct nlattr *tb_data[NUM_MTK_VENDOR_ATTRS_CSI_DATA];
62 struct nlattr *attr;
63 struct nlattr *cur;
64 int rem, idx;
65 struct csi_data *c = &csi[csi_idx];
66
67 attr = unl_find_attr(&unl, msg, NL80211_ATTR_VENDOR_DATA);
68 if (!attr) {
69 fprintf(stderr, "Testdata attribute not found\n");
70 return NL_SKIP;
71 }
72
73 nla_parse_nested(tb, MTK_VENDOR_ATTR_CSI_CTRL_MAX,
74 attr, csi_ctrl_policy);
75
76 if (!tb[MTK_VENDOR_ATTR_CSI_CTRL_DATA])
77 return NL_SKIP;
78
79 nla_parse_nested(tb_data, MTK_VENDOR_ATTR_CSI_DATA_MAX,
80 tb[MTK_VENDOR_ATTR_CSI_CTRL_DATA], csi_data_policy);
81
82 if (!(tb_data[MTK_VENDOR_ATTR_CSI_DATA_VER] &&
83 tb_data[MTK_VENDOR_ATTR_CSI_DATA_TS] &&
84 tb_data[MTK_VENDOR_ATTR_CSI_DATA_RSSI] &&
85 tb_data[MTK_VENDOR_ATTR_CSI_DATA_SNR] &&
86 tb_data[MTK_VENDOR_ATTR_CSI_DATA_BW] &&
87 tb_data[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX] &&
88 tb_data[MTK_VENDOR_ATTR_CSI_DATA_TA] &&
89 tb_data[MTK_VENDOR_ATTR_CSI_DATA_I] &&
90 tb_data[MTK_VENDOR_ATTR_CSI_DATA_Q] &&
91 tb_data[MTK_VENDOR_ATTR_CSI_DATA_INFO] &&
92 tb_data[MTK_VENDOR_ATTR_CSI_DATA_MODE] &&
93 tb_data[MTK_VENDOR_ATTR_CSI_DATA_H_IDX])) {
94 fprintf(stderr, "Attributes error for CSI data\n");
95 return NL_SKIP;
96 }
97
98 c->rssi = nla_get_u8(tb_data[MTK_VENDOR_ATTR_CSI_DATA_RSSI]);
99 c->snr = nla_get_u8(tb_data[MTK_VENDOR_ATTR_CSI_DATA_SNR]);
100 c->data_bw = nla_get_u8(tb_data[MTK_VENDOR_ATTR_CSI_DATA_BW]);
101 c->pri_ch_idx = nla_get_u8(tb_data[MTK_VENDOR_ATTR_CSI_DATA_CH_IDX]);
102 c->rx_mode = nla_get_u8(tb_data[MTK_VENDOR_ATTR_CSI_DATA_MODE]);
103
104 c->tx_idx = nla_get_u16(tb_data[MTK_VENDOR_ATTR_CSI_DATA_TX_ANT]);
105 c->rx_idx = nla_get_u16(tb_data[MTK_VENDOR_ATTR_CSI_DATA_RX_ANT]);
106
107 c->info = nla_get_u32(tb_data[MTK_VENDOR_ATTR_CSI_DATA_INFO]);
108 c->h_idx = nla_get_u32(tb_data[MTK_VENDOR_ATTR_CSI_DATA_H_IDX]);
109
110 c->ts = nla_get_u64(tb_data[MTK_VENDOR_ATTR_CSI_DATA_TS]);
111
112 idx = 0;
113 nla_for_each_nested(cur, tb_data[MTK_VENDOR_ATTR_CSI_DATA_TA], rem) {
114 c->ta[idx++] = nla_get_u8(cur);
115 }
116
117 idx = 0;
118 nla_for_each_nested(cur, tb_data[MTK_VENDOR_ATTR_CSI_DATA_I], rem) {
119 c->data_i[idx++] = nla_get_u16(cur);
120 }
121
122 idx = 0;
123 nla_for_each_nested(cur, tb_data[MTK_VENDOR_ATTR_CSI_DATA_Q], rem) {
124 c->data_q[idx++] = nla_get_u16(cur);
125 }
126
127 csi_idx++;
128
129 return NL_SKIP;
130}
131
132static int mt76_csi_to_json(const char *name)
133{
134#define MAX_BUF_SIZE 6000
135 FILE *f;
136 int i;
137
138 f = fopen(name, "a+");
139 if (!f) {
140 printf("open failure");
141 return 1;
142 }
143
144 fwrite("[", 1, 1, f);
145
146 for (i = 0; i < csi_idx; i++) {
147 char *pos, *buf = malloc(MAX_BUF_SIZE);
148 struct csi_data *c = &csi[i];
149 int j;
150
151 pos = buf;
152
153 pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
154
155 pos += snprintf(pos, MAX_BUF_SIZE, "%ld,", c->ts);
156 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]);
157
158 pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->rssi);
159 pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->snr);
160 pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->data_bw);
161 pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->pri_ch_idx);
162 pos += snprintf(pos, MAX_BUF_SIZE, "%u,", c->rx_mode);
163 pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->tx_idx);
164 pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->rx_idx);
165 pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->h_idx);
166 pos += snprintf(pos, MAX_BUF_SIZE, "%d,", c->info);
167
168 pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
169 for (j = 0; j < 256; j++) {
170 pos += snprintf(pos, MAX_BUF_SIZE, "%d", c->data_i[j]);
171 if (j != 255)
172 pos += snprintf(pos, MAX_BUF_SIZE, ",");
173 }
174 pos += snprintf(pos, MAX_BUF_SIZE, "%c,", ']');
175
176 pos += snprintf(pos, MAX_BUF_SIZE, "%c", '[');
177 for (j = 0; j < 256; j++) {
178 pos += snprintf(pos, MAX_BUF_SIZE, "%d", c->data_q[j]);
179 if (j != 255)
180 pos += snprintf(pos, MAX_BUF_SIZE, ",");
181 }
182 pos += snprintf(pos, MAX_BUF_SIZE, "%c", ']');
183
184 pos += snprintf(pos, MAX_BUF_SIZE, "%c", ']');
185 if (i != csi_idx - 1)
186 pos += snprintf(pos, MAX_BUF_SIZE, ",");
187
188 fwrite(buf, 1, pos - buf, f);
189 free(buf);
190 }
191
192 fwrite("]", 1, 1, f);
193
194 fclose(f);
195}
196
197static int mt76_dump(int idx, int argc, char **argv)
198{
199 int pkt_num, ret, i;
200 struct nl_msg *msg;
201 void *data;
202
203 if (argc < 2)
204 return 1;
205 pkt_num = strtol(argv[0], NULL, 10);
206
207#define CSI_DUMP_PER_NUM 3
208 csi_idx = 0;
209 csi = (struct csi_data *)calloc(pkt_num, sizeof(*csi));
210
211 for (i = 0; i < pkt_num / CSI_DUMP_PER_NUM; i++) {
212 if (unl_genl_init(&unl, "nl80211") < 0) {
213 fprintf(stderr, "Failed to connect to nl80211\n");
214 return 2;
215 }
216
217 msg = unl_genl_msg(&unl, NL80211_CMD_VENDOR, true);
218
219 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, idx) ||
220 nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, MTK_NL80211_VENDOR_ID) ||
221 nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL))
222 return false;
223
224 data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
225
226 if (nla_put_u16(msg, MTK_VENDOR_ATTR_CSI_CTRL_DUMP_NUM, CSI_DUMP_PER_NUM))
227 return false;
228
229 nla_nest_end(msg, data);
230
231 ret = unl_genl_request(&unl, msg, mt76_dump_cb, NULL);
232 if (ret)
233 fprintf(stderr, "nl80211 call failed: %s\n", strerror(-ret));
234
235 unl_free(&unl);
236 }
237
238 mt76_csi_to_json(argv[1]);
239 free(csi);
240
241 return ret;
242}
243
244static int mt76_csi_ctrl(struct nl_msg *msg, int argc, char **argv)
245{
246 int idx = MTK_VENDOR_ATTR_CSI_CTRL_CFG_MODE;
247 char *val, *s1, *s2, *cur;
248 void *data;
249
250 val = strchr(argv[0], '=');
251 if (val)
252 *(val++) = 0;
253
254 s1 = s2 = strdup(val);
255
256 data = nla_nest_start(msg, MTK_VENDOR_ATTR_CSI_CTRL_CFG | NLA_F_NESTED);
257
258 while ((cur = strsep(&s1, ",")) != NULL)
259 nla_put_u8(msg, idx++, strtoul(cur, NULL, 0));
260
261 nla_nest_end(msg, data);
262
263 free(s2);
264
265 if (argc == 2 &&
266 !strncmp(argv[1], "mac_addr", strlen("mac_addr"))) {
267 u8 a[ETH_ALEN];
268 int matches, i;
269
270 val = strchr(argv[1], '=');
271 if (val)
272 *(val++) = 0;
273
274 matches = sscanf(val, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
275 a, a+1, a+2, a+3, a+4, a+5);
276
277 if (matches != ETH_ALEN)
278 return -EINVAL;
279
280 data = nla_nest_start(msg, MTK_VENDOR_ATTR_CSI_CTRL_MAC_ADDR | NLA_F_NESTED);
281 for (i = 0; i < ETH_ALEN; i++)
282 nla_put_u8(msg, i, a[i]);
283
284 nla_nest_end(msg, data);
285 }
286
287 return 0;
288}
289
290static int mt76_set(int idx, int argc, char **argv)
291{
292 struct nl_msg *msg;
293 void *data;
294 int ret;
295
296 if (argc < 1)
297 return 1;
298
299 msg = unl_genl_msg(&unl, NL80211_CMD_VENDOR, false);
300
301 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, idx) ||
302 nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, MTK_NL80211_VENDOR_ID) ||
303 nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_CSI_CTRL))
304 return false;
305
306 data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
307
308 if (!strncmp(argv[0], "csi_ctrl", strlen("csi_ctrl")))
309 mt76_csi_ctrl(msg, argc, argv);
310
311 nla_nest_end(msg, data);
312
313 ret = unl_genl_request(&unl, msg, NULL, NULL);
314 if (ret)
315 fprintf(stderr, "nl80211 call failed: %s\n", strerror(-ret));
316
317 return ret;
318}
319
320int main(int argc, char **argv)
321{
322 const char *cmd;
323 int ret = 0;
324 int idx;
325
326 progname = argv[0];
327 if (argc < 3)
328 usage();
329
330 idx = if_nametoindex(argv[1]);
331 if (!idx) {
332 fprintf(stderr, "%s\n", strerror(errno));
333 return 2;
334 }
335
336 cmd = argv[2];
337 argv += 3;
338 argc -= 3;
339
340 if (!strcmp(cmd, "dump"))
341 ret = mt76_dump(idx, argc, argv);
342 else if (!strcmp(cmd, "set")) {
343 if (unl_genl_init(&unl, "nl80211") < 0) {
344 fprintf(stderr, "Failed to connect to nl80211\n");
345 return 2;
346 }
347
348 ret = mt76_set(idx, argc, argv);
349 unl_free(&unl);
350 }
351 else
352 usage();
353
354 return ret;
355}