generic: routerboot sysfs platform driver
[openwrt/staging/dedeckeh.git] / target / linux / generic / files / drivers / platform / mikrotik / rb_hardconfig.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Driver for MikroTik RouterBoot hard config.
4 *
5 * Copyright (C) 2020 Thibaut VARĂˆNE <hacks+kernel@slashdirt.org>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published
9 * by the Free Software Foundation.
10 *
11 * This driver exposes the data encoded in the "hard_config" flash segment of
12 * MikroTik RouterBOARDs devices. It presents the data in a sysfs folder
13 * named "hard_config". The WLAN calibration data is available on demand via
14 * the 'wlan_data' sysfs file in that folder.
15 *
16 * This driver permanently allocates a chunk of RAM as large as the hard_config
17 * MTD partition, although it is technically possible to operate entirely from
18 * the MTD device without using a local buffer (except when requesting WLAN
19 * calibration data), at the cost of a performance penalty.
20 *
21 * Some constant defines extracted from routerboot.{c,h} by Gabor Juhos
22 * <juhosg@openwrt.org>
23 */
24
25 #include <linux/types.h>
26 #include <linux/init.h>
27 #include <linux/kernel.h>
28 #include <linux/slab.h>
29 #include <linux/errno.h>
30 #include <linux/kobject.h>
31 #include <linux/bitops.h>
32 #include <linux/string.h>
33 #include <linux/mtd/mtd.h>
34 #include <linux/sysfs.h>
35 #include <linux/lzo.h>
36
37 #include "routerboot.h"
38
39 #define RB_HARDCONFIG_VER "0.01"
40 #define RB_HC_PR_PFX "[rb_hardconfig] "
41
42 /* ID values for hardware settings */
43 #define RB_ID_FLASH_INFO 0x03
44 #define RB_ID_MAC_ADDRESS_PACK 0x04
45 #define RB_ID_BOARD_PRODUCT_CODE 0x05
46 #define RB_ID_BIOS_VERSION 0x06
47 #define RB_ID_SDRAM_TIMINGS 0x08
48 #define RB_ID_DEVICE_TIMINGS 0x09
49 #define RB_ID_SOFTWARE_ID 0x0A
50 #define RB_ID_SERIAL_NUMBER 0x0B
51 #define RB_ID_MEMORY_SIZE 0x0D
52 #define RB_ID_MAC_ADDRESS_COUNT 0x0E
53 #define RB_ID_HW_OPTIONS 0x15
54 #define RB_ID_WLAN_DATA 0x16
55 #define RB_ID_BOARD_IDENTIFIER 0x17
56 #define RB_ID_PRODUCT_NAME 0x21
57 #define RB_ID_DEFCONF 0x26
58
59 /* Bit definitions for hardware options */
60 #define RB_HW_OPT_NO_UART BIT(0)
61 #define RB_HW_OPT_HAS_VOLTAGE BIT(1)
62 #define RB_HW_OPT_HAS_USB BIT(2)
63 #define RB_HW_OPT_HAS_ATTINY BIT(3)
64 #define RB_HW_OPT_NO_NAND BIT(14)
65 #define RB_HW_OPT_HAS_LCD BIT(15)
66 #define RB_HW_OPT_HAS_POE_OUT BIT(16)
67 #define RB_HW_OPT_HAS_uSD BIT(17)
68 #define RB_HW_OPT_HAS_SIM BIT(18)
69 #define RB_HW_OPT_HAS_SFP BIT(20)
70 #define RB_HW_OPT_HAS_WIFI BIT(21)
71 #define RB_HW_OPT_HAS_TS_FOR_ADC BIT(22)
72 #define RB_HW_OPT_HAS_PLC BIT(29)
73
74 static struct kobject *hc_kobj;
75 static u8 *hc_buf; // ro buffer after init(): no locking required
76 static size_t hc_buflen;
77
78 /* Array of known hw_options bits with human-friendly parsing */
79 static struct hc_hwopt {
80 const u32 bit;
81 const char *str;
82 } const hc_hwopts[] = {
83 {
84 .bit = RB_HW_OPT_NO_UART,
85 .str = "no UART\t\t",
86 }, {
87 .bit = RB_HW_OPT_HAS_VOLTAGE,
88 .str = "has Vreg\t",
89 }, {
90 .bit = RB_HW_OPT_HAS_USB,
91 .str = "has usb\t\t",
92 }, {
93 .bit = RB_HW_OPT_HAS_ATTINY,
94 .str = "has ATtiny\t",
95 }, {
96 .bit = RB_HW_OPT_NO_NAND,
97 .str = "no NAND\t\t",
98 }, {
99 .bit = RB_HW_OPT_HAS_LCD,
100 .str = "has LCD\t\t",
101 }, {
102 .bit = RB_HW_OPT_HAS_POE_OUT,
103 .str = "has POE out\t",
104 }, {
105 .bit = RB_HW_OPT_HAS_uSD,
106 .str = "has MicroSD\t",
107 }, {
108 .bit = RB_HW_OPT_HAS_SIM,
109 .str = "has SIM\t\t",
110 }, {
111 .bit = RB_HW_OPT_HAS_SFP,
112 .str = "has SFP\t\t",
113 }, {
114 .bit = RB_HW_OPT_HAS_WIFI,
115 .str = "has WiFi\t",
116 }, {
117 .bit = RB_HW_OPT_HAS_TS_FOR_ADC,
118 .str = "has TS ADC\t",
119 }, {
120 .bit = RB_HW_OPT_HAS_PLC,
121 .str = "has PLC\t\t",
122 },
123 };
124
125 static ssize_t hc_tag_show_string(const u8 *pld, u16 pld_len, char *buf)
126 {
127 return snprintf(buf, pld_len+1, "%s\n", pld);
128 }
129
130 static ssize_t hc_tag_show_u32(const u8 *pld, u16 pld_len, char *buf)
131 {
132 char *out = buf;
133 u32 data; // cpu-endian
134
135 /* Caller ensures pld_len > 0 */
136 if (pld_len % sizeof(data))
137 return -EINVAL;
138
139 data = *(u32 *)pld;
140
141 do {
142 out += sprintf(out, "0x%08x\n", data);
143 data++;
144 } while ((pld_len -= sizeof(data)));
145
146 return out - buf;
147 }
148
149 /*
150 * The MAC is stored network-endian on all devices, in 2 32-bit segments:
151 * <XX:XX:XX:XX> <XX:XX:00:00>. Kernel print has us covered.
152 */
153 static ssize_t hc_tag_show_mac(const u8 *pld, u16 pld_len, char *buf)
154 {
155 if (8 != pld_len)
156 return -EINVAL;
157
158 return sprintf(buf, "%pM\n", pld);
159 }
160
161 /*
162 * Print HW options in a human readable way:
163 * The raw number and in decoded form
164 */
165 static ssize_t hc_tag_show_hwoptions(const u8 *pld, u16 pld_len, char *buf)
166 {
167 char *out = buf;
168 u32 data; // cpu-endian
169 int i;
170
171 if (sizeof(data) != pld_len)
172 return -EINVAL;
173
174 data = *(u32 *)pld;
175 out += sprintf(out, "raw\t\t: 0x%08x\n\n", data);
176
177 for (i = 0; i < ARRAY_SIZE(hc_hwopts); i++)
178 out += sprintf(out, "%s: %s\n", hc_hwopts[i].str,
179 (data & hc_hwopts[i].bit) ? "true" : "false");
180
181 return out - buf;
182 }
183
184 static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj,
185 struct bin_attribute *attr, char *buf,
186 loff_t off, size_t count);
187
188 static struct hc_wlan_attr {
189 struct bin_attribute battr;
190 u16 pld_ofs;
191 u16 pld_len;
192 } hc_wlandata_battr = {
193 .battr = __BIN_ATTR(wlan_data, S_IRUSR, hc_wlan_data_bin_read, NULL, 0),
194 };
195
196 static ssize_t hc_attr_show(struct kobject *kobj, struct kobj_attribute *attr,
197 char *buf);
198
199 /* Array of known tags to publish in sysfs */
200 static struct hc_attr {
201 const u16 tag_id;
202 ssize_t (* const tshow)(const u8 *pld, u16 pld_len, char *buf);
203 struct kobj_attribute kattr;
204 u16 pld_ofs;
205 u16 pld_len;
206 } hc_attrs[] = {
207 {
208 .tag_id = RB_ID_FLASH_INFO,
209 .tshow = hc_tag_show_u32,
210 .kattr = __ATTR(flash_info, S_IRUSR, hc_attr_show, NULL),
211 }, {
212 .tag_id = RB_ID_MAC_ADDRESS_PACK,
213 .tshow = hc_tag_show_mac,
214 .kattr = __ATTR(mac_base, S_IRUSR, hc_attr_show, NULL),
215 }, {
216 .tag_id = RB_ID_BOARD_PRODUCT_CODE,
217 .tshow = hc_tag_show_string,
218 .kattr = __ATTR(board_product_code, S_IRUSR, hc_attr_show, NULL),
219 }, {
220 .tag_id = RB_ID_BIOS_VERSION,
221 .tshow = hc_tag_show_string,
222 .kattr = __ATTR(booter_version, S_IRUSR, hc_attr_show, NULL),
223 }, {
224 .tag_id = RB_ID_SERIAL_NUMBER,
225 .tshow = hc_tag_show_string,
226 .kattr = __ATTR(board_serial, S_IRUSR, hc_attr_show, NULL),
227 }, {
228 .tag_id = RB_ID_MEMORY_SIZE,
229 .tshow = hc_tag_show_u32,
230 .kattr = __ATTR(mem_size, S_IRUSR, hc_attr_show, NULL),
231 }, {
232 .tag_id = RB_ID_MAC_ADDRESS_COUNT,
233 .tshow = hc_tag_show_u32,
234 .kattr = __ATTR(mac_count, S_IRUSR, hc_attr_show, NULL),
235 }, {
236 .tag_id = RB_ID_HW_OPTIONS,
237 .tshow = hc_tag_show_hwoptions,
238 .kattr = __ATTR(hw_options, S_IRUSR, hc_attr_show, NULL),
239 }, {
240 .tag_id = RB_ID_WLAN_DATA,
241 .tshow = NULL,
242 }, {
243 .tag_id = RB_ID_BOARD_IDENTIFIER,
244 .tshow = hc_tag_show_string,
245 .kattr = __ATTR(board_identifier, S_IRUSR, hc_attr_show, NULL),
246 }, {
247 .tag_id = RB_ID_PRODUCT_NAME,
248 .tshow = hc_tag_show_string,
249 .kattr = __ATTR(product_name, S_IRUSR, hc_attr_show, NULL),
250 }, {
251 .tag_id = RB_ID_DEFCONF,
252 .tshow = hc_tag_show_string,
253 .kattr = __ATTR(defconf, S_IRUSR, hc_attr_show, NULL),
254 }
255 };
256
257 /*
258 * If the RB_ID_WLAN_DATA payload starts with RB_MAGIC_ERD, then past
259 * that magic number the payload itself contains a routerboot tag node
260 * locating the LZO-compressed calibration data at id 0x1.
261 */
262 static int hc_wlan_data_unpack_erd(const u8 *inbuf, size_t inlen,
263 void *outbuf, size_t *outlen)
264 {
265 u16 lzo_ofs, lzo_len;
266 int ret;
267
268 /* Find embedded tag */
269 ret = routerboot_tag_find(inbuf, inlen, 0x1, // always id 1
270 &lzo_ofs, &lzo_len);
271 if (ret) {
272 pr_debug(RB_HC_PR_PFX "ERD data not found\n");
273 goto fail;
274 }
275
276 if (lzo_len > inlen) {
277 pr_debug(RB_HC_PR_PFX "Invalid ERD data length\n");
278 ret = -EINVAL;
279 goto fail;
280 }
281
282 ret = lzo1x_decompress_safe(inbuf+lzo_ofs, lzo_len, outbuf, outlen);
283 if (ret)
284 pr_debug(RB_HC_PR_PFX "LZO decompression error (%d)\n", ret);
285
286 fail:
287 return ret;
288 }
289
290 static int hc_wlan_data_unpack(const size_t tofs, size_t tlen,
291 void *outbuf, size_t *outlen)
292 {
293 const u8 *lbuf;
294 u32 magic;
295 int ret;
296
297 /* Caller ensure tlen > 0. tofs is aligned */
298 if ((tofs + tlen) > hc_buflen)
299 return -EIO;
300
301 lbuf = hc_buf + tofs;
302 magic = *(u32 *)lbuf;
303
304 ret = -ENODATA;
305 switch (magic) {
306 case RB_MAGIC_ERD:
307 /* Skip magic */
308 lbuf += sizeof(magic);
309 tlen -= sizeof(magic);
310 ret = hc_wlan_data_unpack_erd(lbuf, tlen, outbuf, outlen);
311 break;
312 default:
313 /*
314 * If the RB_ID_WLAN_DATA payload doesn't start with a
315 * magic number, the payload itself is the raw RLE-encoded
316 * calibration data.
317 */
318 ret = routerboot_rle_decode(lbuf, tlen, outbuf, outlen);
319 if (ret)
320 pr_debug(RB_HC_PR_PFX "RLE decoding error (%d)\n", ret);
321 break;
322 }
323
324 return ret;
325 }
326
327 static ssize_t hc_attr_show(struct kobject *kobj, struct kobj_attribute *attr,
328 char *buf)
329 {
330 struct hc_attr *hc_attr;
331 const u8 *pld;
332 u16 pld_len;
333
334 hc_attr = container_of(attr, typeof(*hc_attr), kattr);
335
336 if (!hc_attr->pld_len)
337 return -ENOENT;
338
339 pld = hc_buf + hc_attr->pld_ofs;
340 pld_len = hc_attr->pld_len;
341
342 return hc_attr->tshow(pld, pld_len, buf);
343 }
344
345 /*
346 * This function will allocate and free memory every time it is called. This
347 * is not the fastest way to do this, but since the data is rarely read (mainly
348 * at boot time to load wlan caldata), this makes it possible to save memory for
349 * the system.
350 */
351 static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj,
352 struct bin_attribute *attr, char *buf,
353 loff_t off, size_t count)
354 {
355 struct hc_wlan_attr *hc_wattr;
356 size_t outlen;
357 void *outbuf;
358 int ret;
359
360 hc_wattr = container_of(attr, typeof(*hc_wattr), battr);
361
362 if (!hc_wattr->pld_len)
363 return -ENOENT;
364
365 outlen = RB_ART_SIZE;
366
367 /* Don't bother unpacking if the source is already too large */
368 if (hc_wattr->pld_len > outlen)
369 return -EFBIG;
370
371 outbuf = kmalloc(outlen, GFP_KERNEL);
372 if (!outbuf)
373 return -ENOMEM;
374
375 ret = hc_wlan_data_unpack(hc_wattr->pld_ofs, hc_wattr->pld_len, outbuf, &outlen);
376 if (ret) {
377 kfree(outbuf);
378 return ret;
379 }
380
381 if (off >= outlen) {
382 kfree(outbuf);
383 return 0;
384 }
385
386 if (off + count > outlen)
387 count = outlen - off;
388
389 memcpy(buf, outbuf + off, count);
390
391 kfree(outbuf);
392 return count;
393 }
394
395 int __init rb_hardconfig_init(struct kobject *rb_kobj)
396 {
397 struct mtd_info *mtd;
398 size_t bytes_read, buflen;
399 const u8 *buf;
400 int i, ret;
401 u32 magic;
402
403 // TODO allow override
404 mtd = get_mtd_device_nm(RB_MTD_HARD_CONFIG);
405 if (IS_ERR(mtd))
406 return -ENODEV;
407
408 hc_buflen = mtd->size;
409 hc_buf = kmalloc(hc_buflen, GFP_KERNEL);
410 if (!hc_buf)
411 return -ENOMEM;
412
413 ret = mtd_read(mtd, 0, hc_buflen, &bytes_read, hc_buf);
414
415 if (bytes_read != hc_buflen) {
416 ret = -EIO;
417 goto fail;
418 }
419
420 /* Check we have what we expect */
421 magic = *(const u32 *)hc_buf;
422 if (RB_MAGIC_HARD != magic) {
423 ret = -EINVAL;
424 goto fail;
425 }
426
427 /* Skip magic */
428 buf = hc_buf + sizeof(magic);
429 buflen = hc_buflen - sizeof(magic);
430
431 /* Populate sysfs */
432 ret = -ENOMEM;
433 hc_kobj = kobject_create_and_add(RB_MTD_HARD_CONFIG, rb_kobj);
434 if (!hc_kobj)
435 goto fail;
436
437 /* Locate and publish all known tags */
438 for (i = 0; i < ARRAY_SIZE(hc_attrs); i++) {
439 ret = routerboot_tag_find(buf, buflen, hc_attrs[i].tag_id,
440 &hc_attrs[i].pld_ofs, &hc_attrs[i].pld_len);
441 if (ret) {
442 hc_attrs[i].pld_ofs = hc_attrs[i].pld_len = 0;
443 continue;
444 }
445
446 /* Account for skipped magic */
447 hc_attrs[i].pld_ofs += sizeof(magic);
448
449 /* Special case RB_ID_WLAN_DATA to prep and create the binary attribute */
450 if ((RB_ID_WLAN_DATA == hc_attrs[i].tag_id) && hc_attrs[i].pld_len) {
451 hc_wlandata_battr.pld_ofs = hc_attrs[i].pld_ofs;
452 hc_wlandata_battr.pld_len = hc_attrs[i].pld_len;
453
454 ret = sysfs_create_bin_file(hc_kobj, &hc_wlandata_battr.battr);
455 if (ret)
456 pr_err(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n",
457 hc_wlandata_battr.battr.attr.name, ret);
458 }
459 /* All other tags are published via standard attributes */
460 else {
461 ret = sysfs_create_file(hc_kobj, &hc_attrs[i].kattr.attr);
462 if (ret)
463 pr_err(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n",
464 hc_attrs[i].kattr.attr.name, ret);
465 }
466 }
467
468 pr_info("MikroTik RouterBOARD hardware configuration sysfs driver v" RB_HARDCONFIG_VER "\n");
469
470 return 0;
471
472 fail:
473 kfree(hc_buf);
474 return ret;
475 }
476
477 void __exit rb_hardconfig_exit(void)
478 {
479 kobject_put(hc_kobj);
480 kfree(hc_buf);
481 }