blob: 699a9eea625a3b478cd2b677f7a904cf761665fa [file] [log] [blame]
// SPDX-License-Identifier: ISC
/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/uio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include <poll.h>
#include <fcntl.h>
#include <signal.h>
#include "mt76-test.h"
struct unl unl;
static uint32_t tm_changed[DIV_ROUND_UP(NUM_MT76_TM_ATTRS, 32)];
static const char *progname;
static int phy_lookup_idx(const char *name)
{
char buf[128];
FILE *f;
int len;
snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name);
f = fopen(buf, "r");
if (!f)
return -1;
len = fread(buf, 1, sizeof(buf) - 1, f);
fclose(f);
if (!len)
return -1;
buf[len] = 0;
return atoi(buf);
}
void usage(void)
{
static const char *const commands[] = {
"set <var>=<val> [...]",
"dump [stats]",
"eeprom file",
"eeprom set <addr>=<val> [...]",
"eeprom changes",
"eeprom reset",
};
int i;
fprintf(stderr, "Usage:\n");
for (i = 0; i < ARRAY_SIZE(commands); i++)
printf(" %s phyX %s\n", progname, commands[i]);
exit(1);
}
static int mt76_dump_cb(struct nl_msg *msg, void *arg)
{
struct nlattr *attr;
attr = unl_find_attr(&unl, msg, NL80211_ATTR_TESTDATA);
if (!attr) {
fprintf(stderr, "Testdata attribute not found\n");
return NL_SKIP;
}
msg_field.print(&msg_field, attr);
return NL_SKIP;
}
static int mt76_dump(int phy, int argc, char **argv)
{
struct nl_msg *msg;
void *data;
msg = unl_genl_msg(&unl, NL80211_CMD_TESTMODE, true);
nla_put_u32(msg, NL80211_ATTR_WIPHY, phy);
data = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
for (; argc > 0; argc--, argv++) {
if (!strcmp(argv[0], "stats"))
nla_put_flag(msg, MT76_TM_ATTR_STATS);
}
nla_nest_end(msg, data);
unl_genl_request(&unl, msg, mt76_dump_cb, NULL);
return 0;
}
static inline void tm_set_changed(uint32_t id)
{
tm_changed[id / 32] |= (1U << (id % 32));
}
static inline bool tm_is_changed(uint32_t id)
{
return tm_changed[id / 32] & (1U << (id % 32));
}
static int mt76_set(int phy, int argc, char **argv)
{
const struct tm_field *fields = msg_field.fields;
struct nl_msg *msg;
void *data;
int i, ret;
if (argc < 1)
return 1;
msg = unl_genl_msg(&unl, NL80211_CMD_TESTMODE, false);
nla_put_u32(msg, NL80211_ATTR_WIPHY, phy);
data = nla_nest_start(msg, NL80211_ATTR_TESTDATA);
for (; argc > 0; argc--, argv++) {
char *name = argv[0];
char *val = strchr(name, '=');
if (!val) {
fprintf(stderr, "Invalid argument: %s\n", name);
return 1;
}
*(val++) = 0;
for (i = 0; i < msg_field.len; i++) {
if (!fields[i].parse)
continue;
if (!strcmp(fields[i].name, name))
break;
}
if (i == msg_field.len) {
fprintf(stderr, "Unknown field: %s\n", name);
return 1;
}
if (tm_is_changed(i)) {
fprintf(stderr, "Duplicate field '%s'\n", name);
return 1;
}
if (!fields[i].parse(&fields[i], i, msg, val))
return 1;
tm_set_changed(i);
}
nla_nest_end(msg, data);
ret = unl_genl_request(&unl, msg, NULL, NULL);
if (ret)
fprintf(stderr, "nl80211 call failed: %s\n", strerror(-ret));
return ret;
}
int main(int argc, char **argv)
{
const char *cmd, *phyname;
int phy;
int ret = 0;
progname = argv[0];
if (argc < 3)
usage();
if (unl_genl_init(&unl, "nl80211") < 0) {
fprintf(stderr, "Failed to connect to nl80211\n");
return 2;
}
phyname = argv[1];
phy = phy_lookup_idx(phyname);
if (phy < 0) {
fprintf(stderr, "Could not find phy '%s'\n", argv[1]);
return 2;
}
cmd = argv[2];
argv += 3;
argc -= 3;
if (!strcmp(cmd, "dump"))
ret = mt76_dump(phy, argc, argv);
else if (!strcmp(cmd, "set"))
ret = mt76_set(phy, argc, argv);
else if (!strcmp(cmd, "eeprom"))
ret = mt76_eeprom(phy, argc, argv);
else if (!strcmp(cmd, "fwlog"))
ret = mt76_fwlog(phyname, argc, argv);
else
usage();
unl_free(&unl);
return ret;
}