blob: 68ad8d065331e088c2935ea5097278d9dd3b2d1a [file] [log] [blame]
developer0f312e82022-11-01 12:31:52 +08001// SPDX-License-Identifier: ISC
2/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
3#define _GNU_SOURCE
4#include <sys/mman.h>
5#include <sys/stat.h>
6
7#include <stdio.h>
8#include <unistd.h>
9#include <string.h>
10#include <fcntl.h>
11#include <errno.h>
12
13#include "mt76-test.h"
14
15static const char *mtd_part;
16static uint32_t mtd_offset;
17
18static char *eeprom_file;
19static int eeprom_fd = -1;
20unsigned char *eeprom_data;
21
22static int mt76_eeprom_dump_cb(struct nl_msg *msg, void *arg)
23{
24 struct nlattr *tb[NUM_MT76_TM_ATTRS];
25 struct nlattr *attr;
26
27 attr = unl_find_attr(&unl, msg, NL80211_ATTR_TESTDATA);
28 if (!attr)
29 return NL_SKIP;
30
31 nla_parse_nested(tb, MT76_TM_ATTR_MAX, attr, msg_field.policy);
32 if (!tb[MT76_TM_ATTR_MTD_PART] || !tb[MT76_TM_ATTR_MTD_OFFSET])
33 return NL_SKIP;
34
35 mtd_part = strdup(nla_get_string(tb[MT76_TM_ATTR_MTD_PART]));
36 mtd_offset = nla_get_u32(tb[MT76_TM_ATTR_MTD_OFFSET]);
37
38 return NL_SKIP;
39}
40
41static FILE *mtd_open(const char *mtd)
42{
43 char line[128], name[64];
44 FILE *fp;
45 int i;
46
47 fp = fopen("/proc/mtd", "r");
48 if (!fp)
49 return NULL;
50
51 snprintf(name, sizeof(name), "\"%s\"", mtd);
52 while (fgets(line, sizeof(line), fp)) {
53 if (!sscanf(line, "mtd%d:", &i) || !strstr(line, name))
54 continue;
55
56 snprintf(line, sizeof(line), "/dev/mtd%d", i);
57 fclose(fp);
58 return fopen(line, "r");
59 }
60 fclose(fp);
61
62 return NULL;
63}
64
65
66static int
67mt76_eeprom_create_file(void)
68{
69 char buf[1024];
70 ssize_t len;
71 FILE *f;
72 int fd;
73
74 f = mtd_open(mtd_part);
75 if (!f) {
76 fprintf(stderr, "Failed to open MTD device\n");
77 return -1;
78 }
79
80 fd = open(eeprom_file, O_RDWR | O_CREAT | O_EXCL, 00644);
81 if (fd < 0)
82 goto out;
83
84 while ((len = fread(buf, 1, sizeof(buf), f)) > 0) {
85 ssize_t w;
86
87retry:
88 w = write(fd, buf, len);
89 if (w > 0)
90 continue;
91
92 if (errno == EINTR)
93 goto retry;
94
95 perror("write");
96 unlink(eeprom_file);
97 close(fd);
98 fd = -1;
99 goto out;
100 }
101
102 lseek(fd, 0, SEEK_SET);
103
104out:
105 fclose(f);
106 return fd;
107}
108
109static bool
110mt76_eeprom_file_exists(void)
111{
112 struct stat st;
113
114 return stat(eeprom_file, &st) == 0;
115}
116
117static int
118mt76_eeprom_init_file(void)
119{
120 int fd;
121
122 if (!mt76_eeprom_file_exists())
123 return mt76_eeprom_create_file();
124
125 fd = open(eeprom_file, O_RDWR);
126 if (fd < 0)
127 perror("open");
128
129 return fd;
130}
131
132int mt76_eeprom_init(int phy)
133{
134 struct nl_msg *msg;
135
136 msg = unl_genl_msg(&unl, NL80211_CMD_TESTMODE, true);
137 nla_put_u32(msg, NL80211_ATTR_WIPHY, phy);
138 unl_genl_request(&unl, msg, mt76_eeprom_dump_cb, NULL);
139
140 if (!mtd_part) {
141 fprintf(stderr, "Could not find MTD partition information\n");
142 return -1;
143 }
144
145 eeprom_file = malloc(sizeof(EEPROM_FILE_PATH_FMT) + strlen(mtd_part));
146 sprintf(eeprom_file, EEPROM_FILE_PATH_FMT, mtd_part);
147
148 eeprom_fd = mt76_eeprom_init_file();
149 if (eeprom_fd < 0)
150 return -1;
151
152 eeprom_data = mmap(NULL, EEPROM_PART_SIZE, PROT_READ | PROT_WRITE,
153 MAP_SHARED, eeprom_fd, mtd_offset);
154 if (!eeprom_data) {
155 perror("mmap");
156 close(eeprom_fd);
157 return -1;
158 }
159
160 return 0;
161}
162
163void mt76_eeprom_close(void)
164{
165 if (eeprom_fd < 0)
166 return;
167
168 msync(eeprom_data, EEPROM_PART_SIZE, MS_SYNC);
169 munmap(eeprom_data, EEPROM_PART_SIZE);
170 close(eeprom_fd);
171 eeprom_fd = -1;
172}
173
174static int
175mt76_eeprom_set(int argc, char **argv)
176{
177 for (; argc > 0; argc--, argv++) {
178 char *addr_str = argv[0];
179 char *val_str = strchr(addr_str, '=');
180 unsigned long addr, val;
181 char *err;
182
183 if (!val_str) {
184 fprintf(stderr, "Invalid argument: %s\n", addr_str);
185 return 1;
186 }
187
188 *(val_str++) = 0;
189
190 addr = strtoul(addr_str, &err, 0);
191 if ((err && *err) || addr >= EEPROM_PART_SIZE) {
192 fprintf(stderr, "Invalid address: %s\n", addr_str);
193 return 1;
194 }
195
196 val = strtoul(val_str, &err, 0);
197 if ((err && *err) || val >= 0xff) {
198 fprintf(stderr, "Invalid value: %s\n", val_str);
199 return 1;
200 }
201
202 eeprom_data[addr] = val;
203 }
204
205 return 0;
206}
207
208static int
209mt76_eeprom_changes(void)
210{
211 unsigned char *buf;
212 FILE *f;
213 int i;
214
215 f = mtd_open(mtd_part);
216 if (!f) {
217 fprintf(stderr, "Cannot open MTD device\n");
218 return 1;
219 }
220
221 buf = malloc(EEPROM_PART_SIZE);
222 fseek(f, mtd_offset, SEEK_SET);
223 fread(buf, 1, EEPROM_PART_SIZE, f);
224 for (i = 0; i < EEPROM_PART_SIZE; i++) {
225 if (buf[i] == eeprom_data[i])
226 continue;
227
228 printf("[%04x] %02x => %02x\n", i, buf[i], eeprom_data[i]);
229 }
230 free(buf);
231
232 return 0;
233}
234
235
236int mt76_eeprom(int phy, int argc, char **argv)
237{
238 const char *cmd;
239 int ret = 0;
240
241 if (argc < 1)
242 usage();
243
244 if (mt76_eeprom_init(phy))
245 return 1;
246
247 cmd = argv[0];
248 argv++;
249 argc--;
250
251 if (!strcmp(cmd, "file"))
252 printf("%s\n", eeprom_file);
253 else if (!strcmp(cmd, "set"))
254 ret = mt76_eeprom_set(argc, argv);
255 else if (!strcmp(cmd, "reset"))
256 unlink(eeprom_file);
257 else if (!strcmp(cmd, "changes"))
258 ret = mt76_eeprom_changes();
259
260 mt76_eeprom_close();
261
262 return ret;
263}