blob: e3f690896a086a4239a4e7325e21a8996307159e [file] [log] [blame]
developer0f312e82022-11-01 12:31:52 +08001// SPDX-License-Identifier: ISC
2/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
3#define _GNU_SOURCE
4#include "mt76-test.h"
5
6static char prefix[64];
7
8static const char * const testmode_state[] = {
9 [MT76_TM_STATE_OFF] = "off",
10 [MT76_TM_STATE_IDLE] = "idle",
11 [MT76_TM_STATE_TX_FRAMES] = "tx_frames",
12 [MT76_TM_STATE_RX_FRAMES] = "rx_frames",
13};
14
15static const char * const testmode_tx_mode[] = {
16 [MT76_TM_TX_MODE_CCK] = "cck",
17 [MT76_TM_TX_MODE_OFDM] = "ofdm",
18 [MT76_TM_TX_MODE_HT] = "ht",
19 [MT76_TM_TX_MODE_VHT] = "vht",
20 [MT76_TM_TX_MODE_HE_SU] = "he_su",
21 [MT76_TM_TX_MODE_HE_EXT_SU] = "he_ext_su",
22 [MT76_TM_TX_MODE_HE_TB] = "he_tb",
23 [MT76_TM_TX_MODE_HE_MU] = "he_mu",
24};
25
26static void print_enum(const struct tm_field *field, struct nlattr *attr)
27{
28 unsigned int i = nla_get_u8(attr);
29
30 if (i < field->enum_len && field->enum_str[i])
31 printf("%s", field->enum_str[i]);
32 else
33 printf("unknown (%d)", i);
34}
35
36static bool parse_enum(const struct tm_field *field, int idx,
37 struct nl_msg *msg, const char *val)
38{
39 int i;
40
41 for (i = 0; i < field->enum_len; i++) {
42 if (strcmp(field->enum_str[i], val) != 0)
43 continue;
44
45 if (nla_put_u8(msg, idx, i))
46 return false;
47
48 return true;
49 }
50
51 printf("Invalid value for parameter '%s': %s\n", field->name, val);
52 printf("Possible values:");
53 for (i = 0; i < field->enum_len; i++)
54 printf(" %s", field->enum_str[i]);
55 printf("\n");
56
57 return false;
58}
59
60static bool parse_u8(const struct tm_field *field, int idx,
61 struct nl_msg *msg, const char *val)
62{
63 return !nla_put_u8(msg, idx, strtoul(val, NULL, 0));
64}
65
66static void print_u8(const struct tm_field *field, struct nlattr *attr)
67{
68 printf("%d", nla_get_u8(attr));
69}
70
71static void print_s8(const struct tm_field *field, struct nlattr *attr)
72{
73 printf("%d", (int8_t)nla_get_u8(attr));
74}
75
76static bool parse_u32(const struct tm_field *field, int idx,
77 struct nl_msg *msg, const char *val)
78{
79 return !nla_put_u32(msg, idx, strtoul(val, NULL, 0));
80}
81
82static void print_s32(const struct tm_field *field, struct nlattr *attr)
83{
84 printf("%d", (int32_t)nla_get_u32(attr));
85}
86
87static void print_u32(const struct tm_field *field, struct nlattr *attr)
88{
89 printf("%d", nla_get_u32(attr));
90}
91
92static void print_u64(const struct tm_field *field, struct nlattr *attr)
93{
94 printf("%lld", (unsigned long long)nla_get_u64(attr));
95}
96
97static bool parse_flag(const struct tm_field *field, int idx,
98 struct nl_msg *msg, const char *val)
99{
100 bool set = strtoul(val, NULL, 0);
101
102 if (!set)
103 return true;
104
105 return !nla_put_flag(msg, idx);
106}
107
108static void print_string(const struct tm_field *field, struct nlattr *attr)
109{
110 printf("%s", nla_get_string(attr));
111}
112
113static bool parse_array(const struct tm_field *field, int idx,
114 struct nl_msg *msg, const char *val)
115{
116 bool ret = true;
117 char *str, *cur, *ap;
118 void *a;
119
120 ap = str = strdup(val);
121
122 a = nla_nest_start(msg, idx);
123
124 idx = 0;
125 while ((cur = strsep(&ap, ",")) != NULL) {
126 ret = field->parse2(field, idx++, msg, cur);
127 if (!ret)
128 break;
129 }
130
131 nla_nest_end(msg, a);
132
133 free(str);
134
135 return ret;
136}
137
138static void print_array(const struct tm_field *field, struct nlattr *attr)
139{
140 struct nlattr *cur;
141 int idx = 0;
142 int rem;
143
144 nla_for_each_nested(cur, attr, rem) {
145 if (idx++ > 0)
146 printf(",");
147 if (nla_len(cur) != 1)
148 continue;
149 field->print2(field, cur);
150 }
151}
152
153static void print_nested(const struct tm_field *field, struct nlattr *attr)
154{
155 struct nlattr **tb = alloca(field->len * sizeof(struct nlattr *));
156 const struct tm_field *fields = field->fields;
157 int i;
158
159 nla_parse_nested(tb, field->len - 1, attr, field->policy);
160 for (i = 0; i < field->len; i++) {
161 int prefix_len = 0;
162
163 if (!tb[i])
164 continue;
165
166 if (!fields[i].print)
167 continue;
168
169 if (fields[i].name)
170 printf("%s%s=", prefix, fields[i].name);
171
172 if (fields[i].prefix) {
173 prefix_len = strlen(prefix);
174 strncat(prefix + prefix_len, fields[i].prefix,
175 sizeof(prefix) - prefix_len - 1);
176 }
177
178 fields[i].print(&fields[i], tb[i]);
179 if (fields[i].prefix)
180 prefix[prefix_len] = 0;
181
182 if (fields[i].name)
183 printf("\n");
184 }
185
186 if (field->print_extra)
187 field->print_extra(field, tb);
188}
189
190static void print_extra_stats(const struct tm_field *field, struct nlattr **tb)
191{
192 float total, failed;
193
194 if (!tb[MT76_TM_STATS_ATTR_RX_PACKETS] ||
195 !tb[MT76_TM_STATS_ATTR_RX_FCS_ERROR])
196 return;
197
198 total = nla_get_u64(tb[MT76_TM_STATS_ATTR_RX_PACKETS]);
199 failed = nla_get_u64(tb[MT76_TM_STATS_ATTR_RX_FCS_ERROR]);
200
201 printf("%srx_per=%.02f%%\n", prefix, 100 * failed / total);
202}
203
204
205#define FIELD_GENERIC(_field, _name, ...) \
206 [FIELD_NAME(_field)] = { \
207 .name = _name, \
208 ##__VA_ARGS__ \
209 }
210
211#define FIELD_RO(_type, _field, _name, ...) \
212 FIELD_GENERIC(_field, _name, \
213 .print = print_##_type, \
214 ##__VA_ARGS__ \
215 )
216
217#define FIELD(_type, _field, _name, ...) \
218 FIELD_RO(_type, _field, _name, \
219 .parse = parse_##_type, \
220 ##__VA_ARGS__ \
221 )
222
223#define FIELD_FLAG(_field, _name) \
224 FIELD_GENERIC(_field, _name, .parse = parse_flag)
225
226#define FIELD_ENUM(_field, _name, _vals) \
227 FIELD(enum, _field, _name, \
228 .enum_str = _vals, \
229 .enum_len = ARRAY_SIZE(_vals) \
230 )
231
232#define FIELD_ARRAY_RO(_type, _field, _name, ...) \
233 FIELD(array, _field, _name, \
234 .print2 = print_##_type, \
235 ##__VA_ARGS__ \
236 )
237
238#define FIELD_ARRAY(_type, _field, _name, ...) \
239 FIELD_ARRAY_RO(_type, _field, _name, \
240 .parse2 = parse_##_type, \
241 ##__VA_ARGS__ \
242 )
243
244#define FIELD_NESTED_RO(_field, _data, _prefix, ...) \
245 FIELD_RO(nested, _field, NULL, \
246 .prefix = _prefix, \
247 .fields = _data ## _fields, \
248 .policy = _data ## _policy, \
249 .len = ARRAY_SIZE(_data ## _fields), \
250 ##__VA_ARGS__ \
251 )
252
253#define FIELD_NAME(_field) MT76_TM_RX_ATTR_##_field
254static const struct tm_field rx_fields[NUM_MT76_TM_RX_ATTRS] = {
255 FIELD_RO(s32, FREQ_OFFSET, "freq_offset"),
256 FIELD_ARRAY_RO(u8, RCPI, "rcpi"),
257 FIELD_ARRAY_RO(s8, IB_RSSI, "ib_rssi"),
258 FIELD_ARRAY_RO(s8, WB_RSSI, "wb_rssi"),
259 FIELD_RO(s8, SNR, "snr"),
260};
261static struct nla_policy rx_policy[NUM_MT76_TM_RX_ATTRS] = {
262 [MT76_TM_RX_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
263 [MT76_TM_RX_ATTR_RCPI] = { .type = NLA_NESTED },
264 [MT76_TM_RX_ATTR_IB_RSSI] = { .type = NLA_NESTED },
265 [MT76_TM_RX_ATTR_WB_RSSI] = { .type = NLA_NESTED },
266 [MT76_TM_RX_ATTR_SNR] = { .type = NLA_U8 },
267};
268#undef FIELD_NAME
269
270#define FIELD_NAME(_field) MT76_TM_STATS_ATTR_##_field
271static const struct tm_field stats_fields[NUM_MT76_TM_STATS_ATTRS] = {
272 FIELD_RO(u32, TX_PENDING, "tx_pending"),
273 FIELD_RO(u32, TX_QUEUED, "tx_queued"),
274 FIELD_RO(u32, TX_DONE, "tx_done"),
275 FIELD_RO(u64, RX_PACKETS, "rx_packets"),
276 FIELD_RO(u64, RX_FCS_ERROR, "rx_fcs_error"),
277 FIELD_NESTED_RO(LAST_RX, rx, "last_"),
278};
279static struct nla_policy stats_policy[NUM_MT76_TM_STATS_ATTRS] = {
280 [MT76_TM_STATS_ATTR_TX_PENDING] = { .type = NLA_U32 },
281 [MT76_TM_STATS_ATTR_TX_QUEUED] = { .type = NLA_U32 },
282 [MT76_TM_STATS_ATTR_TX_DONE] = { .type = NLA_U32 },
283 [MT76_TM_STATS_ATTR_RX_PACKETS] = { .type = NLA_U64 },
284 [MT76_TM_STATS_ATTR_RX_FCS_ERROR] = { .type = NLA_U64 },
285};
286#undef FIELD_NAME
287
288#define FIELD_NAME(_field) MT76_TM_ATTR_##_field
289static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
290 FIELD_FLAG(RESET, "reset"),
291 FIELD_ENUM(STATE, "state", testmode_state),
292 FIELD_RO(string, MTD_PART, "mtd_part"),
293 FIELD_RO(u32, MTD_OFFSET, "mtd_offset"),
294 FIELD(u32, TX_COUNT, "tx_count"),
295 FIELD(u32, TX_LENGTH, "tx_length"),
296 FIELD_ENUM(TX_RATE_MODE, "tx_rate_mode", testmode_tx_mode),
297 FIELD(u8, TX_RATE_NSS, "tx_rate_nss"),
298 FIELD(u8, TX_RATE_IDX, "tx_rate_idx"),
299 FIELD(u8, TX_RATE_SGI, "tx_rate_sgi"),
300 FIELD(u8, TX_RATE_LDPC, "tx_rate_ldpc"),
301 FIELD(u8, TX_RATE_STBC, "tx_rate_stbc"),
302 FIELD(u8, TX_LTF, "tx_ltf"),
303 FIELD(u8, TX_POWER_CONTROL, "tx_power_control"),
304 FIELD_ARRAY(u8, TX_POWER, "tx_power"),
305 FIELD(u8, TX_ANTENNA, "tx_antenna"),
306 FIELD(u32, FREQ_OFFSET, "freq_offset"),
307 FIELD_NESTED_RO(STATS, stats, "",
308 .print_extra = print_extra_stats),
309};
310#undef FIELD_NAME
311
312static struct nla_policy testdata_policy[NUM_MT76_TM_ATTRS] = {
313 [MT76_TM_ATTR_STATE] = { .type = NLA_U8 },
314 [MT76_TM_ATTR_MTD_PART] = { .type = NLA_STRING },
315 [MT76_TM_ATTR_MTD_OFFSET] = { .type = NLA_U32 },
316 [MT76_TM_ATTR_TX_COUNT] = { .type = NLA_U32 },
317 [MT76_TM_ATTR_TX_LENGTH] = { .type = NLA_U32 },
318 [MT76_TM_ATTR_TX_RATE_MODE] = { .type = NLA_U8 },
319 [MT76_TM_ATTR_TX_RATE_NSS] = { .type = NLA_U8 },
320 [MT76_TM_ATTR_TX_RATE_IDX] = { .type = NLA_U8 },
321 [MT76_TM_ATTR_TX_RATE_SGI] = { .type = NLA_U8 },
322 [MT76_TM_ATTR_TX_RATE_LDPC] = { .type = NLA_U8 },
323 [MT76_TM_ATTR_TX_RATE_STBC] = { .type = NLA_U8 },
324 [MT76_TM_ATTR_TX_LTF] = { .type = NLA_U8 },
325 [MT76_TM_ATTR_TX_POWER_CONTROL] = { .type = NLA_U8 },
326 [MT76_TM_ATTR_TX_ANTENNA] = { .type = NLA_U8 },
327 [MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
328 [MT76_TM_ATTR_STATS] = { .type = NLA_NESTED },
329};
330
331const struct tm_field msg_field = {
332 .print = print_nested,
333 .fields = testdata_fields,
334 .policy = testdata_policy,
335 .len = ARRAY_SIZE(testdata_fields)
336};