[][atenl: add package]
[Description]
Add atenl package, a userspace daemon for mt76 testmode.
atenl acts as an intermediate for HQADLL command and mt76 testmode
(implemented with NL80211_CMD_TESTMODE), which provides transparency for
the usage of QA-tool and Litepoint on mt76.
[Release-log]
N/A
Change-Id: If11e67b36dd7c3ef9629e824bc26ed4f16f34dca
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/5553443
diff --git a/feed/atenl/src/main.c b/feed/atenl/src/main.c
new file mode 100644
index 0000000..ec0a320
--- /dev/null
+++ b/feed/atenl/src/main.c
@@ -0,0 +1,274 @@
+/* Copyright (C) 2021-2022 Mediatek Inc. */
+
+#include <signal.h>
+#include <sys/select.h>
+#include <sys/wait.h>
+#include "atenl.h"
+
+static const char *progname;
+bool atenl_enable;
+
+void sig_handler(int signum)
+{
+ atenl_enable = false;
+}
+
+void atenl_init_signals()
+{
+ if (signal(SIGINT, sig_handler) == SIG_ERR)
+ goto out;
+ if (signal(SIGTERM, sig_handler) == SIG_ERR)
+ goto out;
+ if (signal(SIGABRT, sig_handler) == SIG_ERR)
+ goto out;
+ if (signal(SIGUSR1, sig_handler) == SIG_ERR)
+ goto out;
+ if (signal(SIGUSR2, sig_handler) == SIG_ERR)
+ goto out;
+
+ return;
+out:
+ perror("signal");
+}
+
+static int phy_lookup_idx(const char *name)
+{
+ char buf[128];
+ FILE *f;
+ size_t len;
+ int ret;
+
+ ret = snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name);
+ if (snprintf_error(sizeof(buf), ret))
+ return -1;
+
+ 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);
+}
+
+static void usage(void)
+{
+ fprintf(stderr, "Usage:\n");
+ printf(" %s [-u] [-i phyX]\n", progname);
+ printf("options:\n"
+ " -h = show help text\n"
+ " -i = phy name of driver interface, please use first phy for dbdc\n"
+ " -u = use unicast to respond to HQADLL\n");
+ printf("examples:\n"
+ " %s -u -i phy0\n", progname);
+
+ exit(EXIT_FAILURE);
+}
+
+static int atenl_parent_work(struct atenl *an)
+{
+ int sock_eth = an->sock_eth;
+ int count, ret = 0;
+ fd_set readfds;
+
+ atenl_info("[%d]%s: start for receiving HQA commands\n", getpid(), __func__);
+
+ while (atenl_enable) {
+ FD_ZERO(&readfds);
+ FD_SET(sock_eth, &readfds);
+ count = select(sock_eth + 1, &readfds, NULL, NULL, NULL);
+
+ if (count < 0) {
+ atenl_err("%s: select failed, %s\n", __func__, strerror(errno));
+ continue;
+ } else if (count == 0) {
+ usleep(1000);
+ continue;
+ } else {
+ if (FD_ISSET(sock_eth, &readfds)) {
+ struct atenl_data *data = calloc(1, sizeof(struct atenl_data));
+
+ ret = atenl_eth_recv(an, data);
+ if (ret) {
+ kill(an->child_pid, SIGUSR1);
+ return ret;
+ }
+
+ ret = atenl_hqa_recv(an, data);
+ if (ret < 0) {
+ kill(an->child_pid, SIGUSR1);
+ return ret;
+ }
+
+ free(data);
+ }
+ }
+ }
+
+ atenl_info("[%d]%s: parent work end\n", getpid(), __func__);
+
+ return ret;
+}
+
+static int atenl_child_work(struct atenl *an)
+{
+ int rfd = an->pipefd[PIPE_READ], count;
+ int ret = 0;
+ fd_set readfds;
+
+ atenl_info("[%d]%s: start for sending back results\n", getpid(), __func__);
+
+ while (atenl_enable) {
+ struct atenl_data *data = calloc(1, sizeof(struct atenl_data));
+
+ FD_ZERO(&readfds);
+ FD_SET(rfd, &readfds);
+
+ count = select(FD_SETSIZE, &readfds, NULL, NULL, NULL);
+
+ if (count < 0) {
+ atenl_err("%s: select failed, %s\n", __func__, strerror(errno));
+ continue;
+ } else if (count == 0) {
+ usleep(1000);
+ continue;
+ } else {
+ if (FD_ISSET(rfd, &readfds)) {
+ count = read(rfd, data->buf, RACFG_PKT_MAX_SIZE);
+ atenl_dbg("[%d]PIPE Read %d bytes\n", getpid(), count);
+
+ if (count < 0) {
+ atenl_info("%s: %s\n", __func__, strerror(errno));
+ } else if (count == 0) {
+ continue;
+ } else {
+ int ret;
+
+ ret = atenl_hqa_proc_cmd(an, data);
+ if (ret) {
+ kill(getppid(), SIGUSR2);
+ goto out;
+ }
+
+ ret = atenl_eth_send(an, data);
+ if (ret) {
+ kill(getppid(), SIGUSR2);
+ goto out;
+ }
+ }
+ }
+ }
+ }
+
+out:
+ atenl_info("[%d]%s: child work end\n", getpid(), __func__);
+
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ int opt, phy_idx, ret = 0;
+ char *phy = "phy0", *cmd = NULL;
+ struct atenl *an;
+
+ progname = argv[0];
+
+ an = calloc(1, sizeof(struct atenl));
+ if (!an)
+ return -ENOMEM;
+
+ while(1) {
+ opt = getopt(argc, argv, "hi:uc:");
+ if (opt == -1)
+ break;
+
+ switch (opt) {
+ case 'h':
+ usage();
+ free(an);
+ return 0;
+ case 'i':
+ phy = optarg;
+ break;
+ case 'u':
+ an->unicast = true;
+ printf("Opt: use unicast to send response\n");
+ break;
+ case 'c':
+ cmd = optarg;
+ break;
+ default:
+ fprintf(stderr, "Not supported option\n");
+ break;
+ }
+ }
+
+ phy_idx = phy_lookup_idx(phy);
+ if (phy_idx < 0 || phy_idx > UCHAR_MAX) {
+ fprintf(stderr, "Could not find phy '%s'\n", phy);
+ free(an);
+ return 2;
+ }
+
+ if (cmd) {
+ atenl_eeprom_cmd_handler(an, phy_idx, cmd);
+ goto out;
+ }
+
+ atenl_enable = true;
+ atenl_init_signals();
+
+ /* background ourself */
+ if (!fork()) {
+ pid_t pid;
+
+ ret = atenl_eeprom_init(an, phy_idx);
+ if (ret)
+ goto out;
+
+ ret = atenl_eth_init(an);
+ if (ret)
+ goto out;
+
+ ret = pipe(an->pipefd);
+ if (ret) {
+ perror("Pipe");
+ goto out;
+ }
+
+ pid = fork();
+ an->child_pid = pid;
+ if (pid < 0) {
+ perror("Fork");
+ ret = pid;
+ goto out;
+ } else if (pid == 0) {
+ close(an->pipefd[PIPE_WRITE]);
+ atenl_child_work(an);
+ } else {
+ int status;
+
+ close(an->pipefd[PIPE_READ]);
+ atenl_parent_work(an);
+ waitpid(pid, &status, 0);
+ }
+ } else {
+ usleep(800000);
+ }
+
+ ret = 0;
+out:
+ if (an->sock_eth)
+ close(an->sock_eth);
+ if (an->eeprom_fd || an->eeprom_data)
+ atenl_eeprom_close(an);
+ free(an);
+
+ return ret;
+}