blob: e5d4a1051b5f38863b182c2c7f3bdb41de467c07 [file] [log] [blame]
// SPDX-License-Identifier: ISC
/* Copyright (C) 2022 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"
bool done = false;
static const char *debugfs_path(const char *phyname, const char *file)
{
static char path[256];
snprintf(path, sizeof(path), "/sys/kernel/debug/ieee80211/%s/mt76/%s", phyname, file);
return path;
}
static int mt76_set_fwlog_en(const char *phyname, bool en)
{
FILE *f = fopen(debugfs_path(phyname, "fw_debug_bin"), "w");
if (!f) {
fprintf(stderr, "Could not open fw_debug_bin file\n");
return 1;
}
fprintf(f, "7");
fclose(f);
return 0;
}
int read_retry(int fd, void *buf, int len)
{
int out_len = 0;
int r;
while (len > 0) {
if (done)
return -1;
r = read(fd, buf, len);
if (r < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
return -1;
}
if (!r)
return 0;
out_len += r;
len -= r;
buf += r;
}
return out_len;
}
static void handle_signal(int sig)
{
done = true;
}
int mt76_fwlog(const char *phyname, int argc, char **argv)
{
struct sockaddr_in local = {
.sin_family = AF_INET,
.sin_addr.s_addr = INADDR_ANY,
};
struct sockaddr_in remote = {
.sin_family = AF_INET,
.sin_port = htons(55688),
};
char buf[1504];
int ret = 0;
int yes = 1;
int s, fd;
if (argc < 1) {
fprintf(stderr, "need destination address\n");
return 1;
}
if (!inet_aton(argv[0], &remote.sin_addr)) {
fprintf(stderr, "invalid destination address\n");
return 1;
}
s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (s < 0) {
perror("socket");
return 1;
}
setsockopt(s, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));
if (bind(s, (struct sockaddr *)&local, sizeof(local)) < 0) {
perror("bind");
return 1;
}
if (mt76_set_fwlog_en(phyname, true))
return 1;
fd = open(debugfs_path(phyname, "fwlog_data"), O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Could not open fwlog_data file: %s\n", strerror(errno));
ret = 1;
goto out;
}
signal(SIGTERM, handle_signal);
signal(SIGINT, handle_signal);
signal(SIGQUIT, handle_signal);
while (1) {
struct pollfd pfd = {
.fd = fd,
.events = POLLIN | POLLHUP | POLLERR,
};
uint32_t len;
int r;
if (done)
break;
poll(&pfd, 1, -1);
r = read_retry(fd, &len, sizeof(len));
if (r < 0)
break;
if (!r)
continue;
if (len > sizeof(buf)) {
fprintf(stderr, "Length error: %d > %d\n", len, (int)sizeof(buf));
ret = 1;
break;
}
if (done)
break;
r = read_retry(fd, buf, len);
if (done)
break;
if (r != len) {
fprintf(stderr, "Short read: %d < %d\n", r, len);
ret = 1;
break;
}
/* send buf */
sendto(s, buf, len, 0, (struct sockaddr *)&remote, sizeof(remote));
}
close(fd);
out:
mt76_set_fwlog_en(phyname, false);
return ret;
}