blob: e3f690896a086a4239a4e7325e21a8996307159e [file] [log] [blame]
// SPDX-License-Identifier: ISC
/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
#define _GNU_SOURCE
#include "mt76-test.h"
static char prefix[64];
static const char * const testmode_state[] = {
[MT76_TM_STATE_OFF] = "off",
[MT76_TM_STATE_IDLE] = "idle",
[MT76_TM_STATE_TX_FRAMES] = "tx_frames",
[MT76_TM_STATE_RX_FRAMES] = "rx_frames",
};
static const char * const testmode_tx_mode[] = {
[MT76_TM_TX_MODE_CCK] = "cck",
[MT76_TM_TX_MODE_OFDM] = "ofdm",
[MT76_TM_TX_MODE_HT] = "ht",
[MT76_TM_TX_MODE_VHT] = "vht",
[MT76_TM_TX_MODE_HE_SU] = "he_su",
[MT76_TM_TX_MODE_HE_EXT_SU] = "he_ext_su",
[MT76_TM_TX_MODE_HE_TB] = "he_tb",
[MT76_TM_TX_MODE_HE_MU] = "he_mu",
};
static void print_enum(const struct tm_field *field, struct nlattr *attr)
{
unsigned int i = nla_get_u8(attr);
if (i < field->enum_len && field->enum_str[i])
printf("%s", field->enum_str[i]);
else
printf("unknown (%d)", i);
}
static bool parse_enum(const struct tm_field *field, int idx,
struct nl_msg *msg, const char *val)
{
int i;
for (i = 0; i < field->enum_len; i++) {
if (strcmp(field->enum_str[i], val) != 0)
continue;
if (nla_put_u8(msg, idx, i))
return false;
return true;
}
printf("Invalid value for parameter '%s': %s\n", field->name, val);
printf("Possible values:");
for (i = 0; i < field->enum_len; i++)
printf(" %s", field->enum_str[i]);
printf("\n");
return false;
}
static bool parse_u8(const struct tm_field *field, int idx,
struct nl_msg *msg, const char *val)
{
return !nla_put_u8(msg, idx, strtoul(val, NULL, 0));
}
static void print_u8(const struct tm_field *field, struct nlattr *attr)
{
printf("%d", nla_get_u8(attr));
}
static void print_s8(const struct tm_field *field, struct nlattr *attr)
{
printf("%d", (int8_t)nla_get_u8(attr));
}
static bool parse_u32(const struct tm_field *field, int idx,
struct nl_msg *msg, const char *val)
{
return !nla_put_u32(msg, idx, strtoul(val, NULL, 0));
}
static void print_s32(const struct tm_field *field, struct nlattr *attr)
{
printf("%d", (int32_t)nla_get_u32(attr));
}
static void print_u32(const struct tm_field *field, struct nlattr *attr)
{
printf("%d", nla_get_u32(attr));
}
static void print_u64(const struct tm_field *field, struct nlattr *attr)
{
printf("%lld", (unsigned long long)nla_get_u64(attr));
}
static bool parse_flag(const struct tm_field *field, int idx,
struct nl_msg *msg, const char *val)
{
bool set = strtoul(val, NULL, 0);
if (!set)
return true;
return !nla_put_flag(msg, idx);
}
static void print_string(const struct tm_field *field, struct nlattr *attr)
{
printf("%s", nla_get_string(attr));
}
static bool parse_array(const struct tm_field *field, int idx,
struct nl_msg *msg, const char *val)
{
bool ret = true;
char *str, *cur, *ap;
void *a;
ap = str = strdup(val);
a = nla_nest_start(msg, idx);
idx = 0;
while ((cur = strsep(&ap, ",")) != NULL) {
ret = field->parse2(field, idx++, msg, cur);
if (!ret)
break;
}
nla_nest_end(msg, a);
free(str);
return ret;
}
static void print_array(const struct tm_field *field, struct nlattr *attr)
{
struct nlattr *cur;
int idx = 0;
int rem;
nla_for_each_nested(cur, attr, rem) {
if (idx++ > 0)
printf(",");
if (nla_len(cur) != 1)
continue;
field->print2(field, cur);
}
}
static void print_nested(const struct tm_field *field, struct nlattr *attr)
{
struct nlattr **tb = alloca(field->len * sizeof(struct nlattr *));
const struct tm_field *fields = field->fields;
int i;
nla_parse_nested(tb, field->len - 1, attr, field->policy);
for (i = 0; i < field->len; i++) {
int prefix_len = 0;
if (!tb[i])
continue;
if (!fields[i].print)
continue;
if (fields[i].name)
printf("%s%s=", prefix, fields[i].name);
if (fields[i].prefix) {
prefix_len = strlen(prefix);
strncat(prefix + prefix_len, fields[i].prefix,
sizeof(prefix) - prefix_len - 1);
}
fields[i].print(&fields[i], tb[i]);
if (fields[i].prefix)
prefix[prefix_len] = 0;
if (fields[i].name)
printf("\n");
}
if (field->print_extra)
field->print_extra(field, tb);
}
static void print_extra_stats(const struct tm_field *field, struct nlattr **tb)
{
float total, failed;
if (!tb[MT76_TM_STATS_ATTR_RX_PACKETS] ||
!tb[MT76_TM_STATS_ATTR_RX_FCS_ERROR])
return;
total = nla_get_u64(tb[MT76_TM_STATS_ATTR_RX_PACKETS]);
failed = nla_get_u64(tb[MT76_TM_STATS_ATTR_RX_FCS_ERROR]);
printf("%srx_per=%.02f%%\n", prefix, 100 * failed / total);
}
#define FIELD_GENERIC(_field, _name, ...) \
[FIELD_NAME(_field)] = { \
.name = _name, \
##__VA_ARGS__ \
}
#define FIELD_RO(_type, _field, _name, ...) \
FIELD_GENERIC(_field, _name, \
.print = print_##_type, \
##__VA_ARGS__ \
)
#define FIELD(_type, _field, _name, ...) \
FIELD_RO(_type, _field, _name, \
.parse = parse_##_type, \
##__VA_ARGS__ \
)
#define FIELD_FLAG(_field, _name) \
FIELD_GENERIC(_field, _name, .parse = parse_flag)
#define FIELD_ENUM(_field, _name, _vals) \
FIELD(enum, _field, _name, \
.enum_str = _vals, \
.enum_len = ARRAY_SIZE(_vals) \
)
#define FIELD_ARRAY_RO(_type, _field, _name, ...) \
FIELD(array, _field, _name, \
.print2 = print_##_type, \
##__VA_ARGS__ \
)
#define FIELD_ARRAY(_type, _field, _name, ...) \
FIELD_ARRAY_RO(_type, _field, _name, \
.parse2 = parse_##_type, \
##__VA_ARGS__ \
)
#define FIELD_NESTED_RO(_field, _data, _prefix, ...) \
FIELD_RO(nested, _field, NULL, \
.prefix = _prefix, \
.fields = _data ## _fields, \
.policy = _data ## _policy, \
.len = ARRAY_SIZE(_data ## _fields), \
##__VA_ARGS__ \
)
#define FIELD_NAME(_field) MT76_TM_RX_ATTR_##_field
static const struct tm_field rx_fields[NUM_MT76_TM_RX_ATTRS] = {
FIELD_RO(s32, FREQ_OFFSET, "freq_offset"),
FIELD_ARRAY_RO(u8, RCPI, "rcpi"),
FIELD_ARRAY_RO(s8, IB_RSSI, "ib_rssi"),
FIELD_ARRAY_RO(s8, WB_RSSI, "wb_rssi"),
FIELD_RO(s8, SNR, "snr"),
};
static struct nla_policy rx_policy[NUM_MT76_TM_RX_ATTRS] = {
[MT76_TM_RX_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
[MT76_TM_RX_ATTR_RCPI] = { .type = NLA_NESTED },
[MT76_TM_RX_ATTR_IB_RSSI] = { .type = NLA_NESTED },
[MT76_TM_RX_ATTR_WB_RSSI] = { .type = NLA_NESTED },
[MT76_TM_RX_ATTR_SNR] = { .type = NLA_U8 },
};
#undef FIELD_NAME
#define FIELD_NAME(_field) MT76_TM_STATS_ATTR_##_field
static const struct tm_field stats_fields[NUM_MT76_TM_STATS_ATTRS] = {
FIELD_RO(u32, TX_PENDING, "tx_pending"),
FIELD_RO(u32, TX_QUEUED, "tx_queued"),
FIELD_RO(u32, TX_DONE, "tx_done"),
FIELD_RO(u64, RX_PACKETS, "rx_packets"),
FIELD_RO(u64, RX_FCS_ERROR, "rx_fcs_error"),
FIELD_NESTED_RO(LAST_RX, rx, "last_"),
};
static struct nla_policy stats_policy[NUM_MT76_TM_STATS_ATTRS] = {
[MT76_TM_STATS_ATTR_TX_PENDING] = { .type = NLA_U32 },
[MT76_TM_STATS_ATTR_TX_QUEUED] = { .type = NLA_U32 },
[MT76_TM_STATS_ATTR_TX_DONE] = { .type = NLA_U32 },
[MT76_TM_STATS_ATTR_RX_PACKETS] = { .type = NLA_U64 },
[MT76_TM_STATS_ATTR_RX_FCS_ERROR] = { .type = NLA_U64 },
};
#undef FIELD_NAME
#define FIELD_NAME(_field) MT76_TM_ATTR_##_field
static const struct tm_field testdata_fields[NUM_MT76_TM_ATTRS] = {
FIELD_FLAG(RESET, "reset"),
FIELD_ENUM(STATE, "state", testmode_state),
FIELD_RO(string, MTD_PART, "mtd_part"),
FIELD_RO(u32, MTD_OFFSET, "mtd_offset"),
FIELD(u32, TX_COUNT, "tx_count"),
FIELD(u32, TX_LENGTH, "tx_length"),
FIELD_ENUM(TX_RATE_MODE, "tx_rate_mode", testmode_tx_mode),
FIELD(u8, TX_RATE_NSS, "tx_rate_nss"),
FIELD(u8, TX_RATE_IDX, "tx_rate_idx"),
FIELD(u8, TX_RATE_SGI, "tx_rate_sgi"),
FIELD(u8, TX_RATE_LDPC, "tx_rate_ldpc"),
FIELD(u8, TX_RATE_STBC, "tx_rate_stbc"),
FIELD(u8, TX_LTF, "tx_ltf"),
FIELD(u8, TX_POWER_CONTROL, "tx_power_control"),
FIELD_ARRAY(u8, TX_POWER, "tx_power"),
FIELD(u8, TX_ANTENNA, "tx_antenna"),
FIELD(u32, FREQ_OFFSET, "freq_offset"),
FIELD_NESTED_RO(STATS, stats, "",
.print_extra = print_extra_stats),
};
#undef FIELD_NAME
static struct nla_policy testdata_policy[NUM_MT76_TM_ATTRS] = {
[MT76_TM_ATTR_STATE] = { .type = NLA_U8 },
[MT76_TM_ATTR_MTD_PART] = { .type = NLA_STRING },
[MT76_TM_ATTR_MTD_OFFSET] = { .type = NLA_U32 },
[MT76_TM_ATTR_TX_COUNT] = { .type = NLA_U32 },
[MT76_TM_ATTR_TX_LENGTH] = { .type = NLA_U32 },
[MT76_TM_ATTR_TX_RATE_MODE] = { .type = NLA_U8 },
[MT76_TM_ATTR_TX_RATE_NSS] = { .type = NLA_U8 },
[MT76_TM_ATTR_TX_RATE_IDX] = { .type = NLA_U8 },
[MT76_TM_ATTR_TX_RATE_SGI] = { .type = NLA_U8 },
[MT76_TM_ATTR_TX_RATE_LDPC] = { .type = NLA_U8 },
[MT76_TM_ATTR_TX_RATE_STBC] = { .type = NLA_U8 },
[MT76_TM_ATTR_TX_LTF] = { .type = NLA_U8 },
[MT76_TM_ATTR_TX_POWER_CONTROL] = { .type = NLA_U8 },
[MT76_TM_ATTR_TX_ANTENNA] = { .type = NLA_U8 },
[MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 },
[MT76_TM_ATTR_STATS] = { .type = NLA_NESTED },
};
const struct tm_field msg_field = {
.print = print_nested,
.fields = testdata_fields,
.policy = testdata_policy,
.len = ARRAY_SIZE(testdata_fields)
};