Skip to content

Commit

Permalink
add utility for using testmode support
Browse files Browse the repository at this point in the history
Signed-off-by: Felix Fietkau <[email protected]>
  • Loading branch information
nbd168 committed Jun 22, 2020
1 parent d7467bc commit 3ea5da1
Show file tree
Hide file tree
Showing 7 changed files with 916 additions and 1 deletion.
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
.*
*.o
*.ko
*.mod
*.mod.c
Module.symvers
*.symvers
*.symvers.tmp
modules.order
ipkg-*
CMakeFiles
CMakeCache.txt
cmake_install.cmake
tools/Makefile
tools/mt76-test
13 changes: 13 additions & 0 deletions tools/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 2.8)

PROJECT(mt76-test C)
ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3)

ADD_EXECUTABLE(mt76-test main.c fields.c eeprom.c)
TARGET_LINK_LIBRARIES(mt76-test nl-tiny)

SET(CMAKE_INSTALL_PREFIX /usr)

INSTALL(TARGETS mt76-test
RUNTIME DESTINATION sbin
)
48 changes: 48 additions & 0 deletions tools/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# MT76 testmode utility

This utility can be used to perform some support functions required for Rx/Tx calibration, similar to the ATE command set of the SDK driver.
Its main functionality is setting test parameters and dumping statistics. It can also be used to prepare modified EEPROM data for writing into flash.

## Basic syntax

- Set parameters:
- `mt76-test phy0 set <parameter>=<value>`[...]
- Show current parameter set:
- `mt76-test phy0 dump`
- Show statistics
- `mt76-test phy0 dump stats`

## Running tests

The test state is controlled through the `state` parameter. The following state values are supported:

- `off`: Normal operation (default)
- `idle`: Testmode enabled, but no specific test active
- `tx_frames`: Send a number of packets with configurable rate/txpower
- `rx_frames`: Receive packets and show RSSI and packet count/PER

Setting a state activates it even if the value is the same as before. Setting it to `tx_frames` triggers sending packets immediately. Setting `rx_frames` enables receive mode and can also be used to clear rx statistics.

## Notes

To run tests, you first need to disable all normal interfaces, set up a monitor mode interface and configure it to the channel/bandwidth you intend to use.


## Parameters:

| Parameter name | ATE parameter | Description |
|--|--|--|
| `state` | `ATE` | Test state |
| `tx_count` | `ATETXCNT` | Number of packets to send |
| `tx_length` | `ATETXLEN` | Length of packets to send |
| `tx_rate_mode` | `ATETXMODE` | PHY mode (possible values: `cck`, `ofdm`, `ht`, `vht`) |
| `tx_rate_nss` | | Number of spatial streams (VHT only) |
| `tx_rate_idx` | `ATETXMCS` | MCS or legacy rate index |
| `tx_rate_sgi` | `ATETXGI` | Enable short guard interval |
| `tx_rate_ldpc` | `ATETXLDPC` | Enable LDPC |
| `tx_power_control` | `ATETXPOWERCTRL` | Firmware transmit power control feature |
| `tx_power` | `ATETXPOW0-3` | Per-chain half-dBm transmit power, `0` means default value, e.g. `10,0,0,0` |
| `tx_antenna` | `ATETXANT` | Transmit antenna bitmask |
| `freq_offset` | `ATETXFREQOFFSET` | Frequency offset |


263 changes: 263 additions & 0 deletions tools/eeprom.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
// SPDX-License-Identifier: ISC
/* Copyright (C) 2020 Felix Fietkau <[email protected]> */
#define _GNU_SOURCE
#include <sys/mman.h>
#include <sys/stat.h>

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>

#include "mt76-test.h"

static const char *mtd_part;
static uint32_t mtd_offset;

static char *eeprom_file;
static int eeprom_fd = -1;
unsigned char *eeprom_data;

static int mt76_eeprom_dump_cb(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NUM_MT76_TM_ATTRS];
struct nlattr *attr;

attr = unl_find_attr(&unl, msg, NL80211_ATTR_TESTDATA);
if (!attr)
return NL_SKIP;

nla_parse_nested(tb, MT76_TM_ATTR_MAX, attr, msg_field.policy);
if (!tb[MT76_TM_ATTR_MTD_PART] || !tb[MT76_TM_ATTR_MTD_OFFSET])
return NL_SKIP;

mtd_part = strdup(nla_get_string(tb[MT76_TM_ATTR_MTD_PART]));
mtd_offset = nla_get_u32(tb[MT76_TM_ATTR_MTD_OFFSET]);

return NL_SKIP;
}

static FILE *mtd_open(const char *mtd)
{
char line[128], name[64];
FILE *fp;
int i;

fp = fopen("/proc/mtd", "r");
if (!fp)
return NULL;

snprintf(name, sizeof(name), "\"%s\"", mtd);
while (fgets(line, sizeof(line), fp)) {
if (!sscanf(line, "mtd%d:", &i) || !strstr(line, name))
continue;

snprintf(line, sizeof(line), "/dev/mtd%d", i);
fclose(fp);
return fopen(line, "r");
}
fclose(fp);

return NULL;
}


static int
mt76_eeprom_create_file(void)
{
char buf[1024];
ssize_t len;
FILE *f;
int fd;

f = mtd_open(mtd_part);
if (!f) {
fprintf(stderr, "Failed to open MTD device\n");
return -1;
}

fd = open(eeprom_file, O_RDWR | O_CREAT | O_EXCL);
if (fd < 0)
goto out;

while ((len = fread(buf, 1, sizeof(buf), f)) > 0) {
ssize_t w;

retry:
w = write(fd, buf, len);
if (w > 0)
continue;

if (errno == EINTR)
goto retry;

perror("write");
unlink(eeprom_file);
close(fd);
fd = -1;
goto out;
}

lseek(fd, 0, SEEK_SET);

out:
fclose(f);
return fd;
}

static bool
mt76_eeprom_file_exists(void)
{
struct stat st;

return stat(eeprom_file, &st) == 0;
}

static int
mt76_eeprom_init_file(void)
{
int fd;

if (!mt76_eeprom_file_exists())
return mt76_eeprom_create_file();

fd = open(eeprom_file, O_RDWR);
if (fd < 0)
perror("open");

return fd;
}

int mt76_eeprom_init(int phy)
{
struct nl_msg *msg;

msg = unl_genl_msg(&unl, NL80211_CMD_TESTMODE, true);
nla_put_u32(msg, NL80211_ATTR_WIPHY, phy);
unl_genl_request(&unl, msg, mt76_eeprom_dump_cb, NULL);

if (!mtd_part) {
fprintf(stderr, "Could not find MTD partition information\n");
return -1;
}

eeprom_file = malloc(sizeof(EEPROM_FILE_PATH_FMT) + strlen(mtd_part));
sprintf(eeprom_file, EEPROM_FILE_PATH_FMT, mtd_part);

eeprom_fd = mt76_eeprom_init_file();
if (eeprom_fd < 0)
return -1;

eeprom_data = mmap(NULL, EEPROM_PART_SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED, eeprom_fd, mtd_offset);
if (!eeprom_data) {
perror("mmap");
close(eeprom_fd);
return -1;
}

return 0;
}

void mt76_eeprom_close(void)
{
if (eeprom_fd < 0)
return;

msync(eeprom_data, EEPROM_PART_SIZE, MS_SYNC);
munmap(eeprom_data, EEPROM_PART_SIZE);
close(eeprom_fd);
eeprom_fd = -1;
}

static int
mt76_eeprom_set(int argc, char **argv)
{
for (; argc > 0; argc--, argv++) {
char *addr_str = argv[0];
char *val_str = strchr(addr_str, '=');
unsigned long addr, val;
char *err;

if (!val_str) {
fprintf(stderr, "Invalid argument: %s\n", addr_str);
return 1;
}

*(val_str++) = 0;

addr = strtoul(addr_str, &err, 0);
if ((err && *err) || addr >= EEPROM_PART_SIZE) {
fprintf(stderr, "Invalid address: %s\n", addr_str);
return 1;
}

val = strtoul(val_str, &err, 0);
if ((err && *err) || val >= 0xff) {
fprintf(stderr, "Invalid value: %s\n", val_str);
return 1;
}

eeprom_data[addr] = val;
}

return 0;
}

static int
mt76_eeprom_changes(void)
{
unsigned char *buf;
FILE *f;
int i;

f = mtd_open(mtd_part);
if (!f) {
fprintf(stderr, "Cannot open MTD device\n");
return 1;
}

buf = malloc(EEPROM_PART_SIZE);
fseek(f, mtd_offset, SEEK_SET);
fread(buf, 1, EEPROM_PART_SIZE, f);
for (i = 0; i < EEPROM_PART_SIZE; i++) {
if (buf[i] == eeprom_data[i])
continue;

printf("[%04x] %02x => %02x\n", i, buf[i], eeprom_data[i]);
}
free(buf);

return 0;
}


int mt76_eeprom(int phy, int argc, char **argv)
{
const char *cmd;
int ret = 0;

if (argc < 1)
usage();

if (mt76_eeprom_init(phy))
return 1;

cmd = argv[0];
argv++;
argc--;

if (!strcmp(cmd, "file"))
printf("%s\n", eeprom_file);
else if (!strcmp(cmd, "set"))
ret = mt76_eeprom_set(argc, argv);
else if (!strcmp(cmd, "reset"))
unlink(eeprom_file);
else if (!strcmp(cmd, "changes"))
ret = mt76_eeprom_changes();

mt76_eeprom_close();

return ret;
}
Loading

0 comments on commit 3ea5da1

Please sign in to comment.