generic: platform/mikrotik: rb_hardconfig.c minor fixes
[oweals/openwrt.git] / target / linux / generic / files / drivers / platform / mikrotik / rb_hardconfig.c
1 // SPDX-License-Identifier: GPL-2.0-only
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  * Note: PAGE_SIZE is assumed to be >= 4K, hence the device attribute show
22  * routines need not check for output overflow.
23  *
24  * Some constant defines extracted from routerboot.{c,h} by Gabor Juhos
25  * <juhosg@openwrt.org>
26  */
27
28 #include <linux/types.h>
29 #include <linux/init.h>
30 #include <linux/kernel.h>
31 #include <linux/slab.h>
32 #include <linux/errno.h>
33 #include <linux/kobject.h>
34 #include <linux/bitops.h>
35 #include <linux/string.h>
36 #include <linux/mtd/mtd.h>
37 #include <linux/sysfs.h>
38 #include <linux/lzo.h>
39
40 #include "routerboot.h"
41
42 #define RB_HARDCONFIG_VER               "0.04"
43 #define RB_HC_PR_PFX                    "[rb_hardconfig] "
44
45 /* ID values for hardware settings */
46 #define RB_ID_FLASH_INFO                0x03
47 #define RB_ID_MAC_ADDRESS_PACK          0x04
48 #define RB_ID_BOARD_PRODUCT_CODE        0x05
49 #define RB_ID_BIOS_VERSION              0x06
50 #define RB_ID_SDRAM_TIMINGS             0x08
51 #define RB_ID_DEVICE_TIMINGS            0x09
52 #define RB_ID_SOFTWARE_ID               0x0A
53 #define RB_ID_SERIAL_NUMBER             0x0B
54 #define RB_ID_MEMORY_SIZE               0x0D
55 #define RB_ID_MAC_ADDRESS_COUNT         0x0E
56 #define RB_ID_HW_OPTIONS                0x15
57 #define RB_ID_WLAN_DATA                 0x16
58 #define RB_ID_BOARD_IDENTIFIER          0x17
59 #define RB_ID_PRODUCT_NAME              0x21
60 #define RB_ID_DEFCONF                   0x26
61
62 /* Bit definitions for hardware options */
63 #define RB_HW_OPT_NO_UART               BIT(0)
64 #define RB_HW_OPT_HAS_VOLTAGE           BIT(1)
65 #define RB_HW_OPT_HAS_USB               BIT(2)
66 #define RB_HW_OPT_HAS_ATTINY            BIT(3)
67 #define RB_HW_OPT_PULSE_DUTY_CYCLE      BIT(9)
68 #define RB_HW_OPT_NO_NAND               BIT(14)
69 #define RB_HW_OPT_HAS_LCD               BIT(15)
70 #define RB_HW_OPT_HAS_POE_OUT           BIT(16)
71 #define RB_HW_OPT_HAS_uSD               BIT(17)
72 #define RB_HW_OPT_HAS_SIM               BIT(18)
73 #define RB_HW_OPT_HAS_SFP               BIT(20)
74 #define RB_HW_OPT_HAS_WIFI              BIT(21)
75 #define RB_HW_OPT_HAS_TS_FOR_ADC        BIT(22)
76 #define RB_HW_OPT_HAS_PLC               BIT(29)
77
78 static struct kobject *hc_kobj;
79 static u8 *hc_buf;              // ro buffer after init(): no locking required
80 static size_t hc_buflen;
81
82 /*
83  * For LZOR style WLAN data unpacking.
84  * This binary blob is prepended to the data encoded on some devices as
85  * RB_ID_WLAN_DATA, the result is then first decompressed with LZO, and then
86  * finally RLE-decoded.
87  * This binary blob has been extracted from RouterOS by
88  * https://forum.openwrt.org/u/ius
89  */
90 static const u8 hc_lzor_prefix[] = {
91         0x00, 0x05, 0x4c, 0x4c, 0x44, 0x00, 0x34, 0xfe,
92         0xfe, 0x34, 0x11, 0x3c, 0x1e, 0x3c, 0x2e, 0x3c,
93         0x4c, 0x34, 0x00, 0x52, 0x62, 0x92, 0xa2, 0xb2,
94         0xc3, 0x2a, 0x14, 0x00, 0x00, 0x05, 0xfe, 0x6a,
95         0x3c, 0x16, 0x32, 0x16, 0x11, 0x1e, 0x12, 0x46,
96         0x32, 0x46, 0x11, 0x4e, 0x12, 0x36, 0x32, 0x36,
97         0x11, 0x3e, 0x12, 0x5a, 0x9a, 0x64, 0x00, 0x04,
98         0xfe, 0x10, 0x3c, 0x00, 0x01, 0x00, 0x00, 0x28,
99         0x0c, 0x00, 0x0f, 0xfe, 0x14, 0x00, 0x24, 0x24,
100         0x23, 0x24, 0x24, 0x23, 0x25, 0x22, 0x21, 0x21,
101         0x23, 0x22, 0x21, 0x22, 0x21, 0x2d, 0x38, 0x00,
102         0x0c, 0x25, 0x25, 0x24, 0x25, 0x25, 0x24, 0x23,
103         0x22, 0x21, 0x20, 0x23, 0x21, 0x21, 0x22, 0x21,
104         0x2d, 0x38, 0x00, 0x28, 0xb0, 0x00, 0x00, 0x22,
105         0x00, 0x00, 0xc0, 0xfe, 0x03, 0x00, 0xc0, 0x00,
106         0x62, 0xff, 0x62, 0xff, 0xfe, 0x06, 0x00, 0xbb,
107         0xff, 0xba, 0xff, 0xfe, 0x08, 0x00, 0x9e, 0xff,
108         0xfe, 0x0a, 0x00, 0x53, 0xff, 0xfe, 0x02, 0x00,
109         0x20, 0xff, 0xb1, 0xfe, 0xfe, 0xb2, 0xfe, 0xfe,
110         0xed, 0xfe, 0xfe, 0xfe, 0x04, 0x00, 0x3a, 0xff,
111         0x3a, 0xff, 0xde, 0xfd, 0x5f, 0x04, 0x33, 0xff,
112         0x4c, 0x74, 0x03, 0x05, 0x05, 0xff, 0x6d, 0xfe,
113         0xfe, 0x6d, 0xfe, 0xfe, 0xaf, 0x08, 0x63, 0xff,
114         0x64, 0x6f, 0x08, 0xac, 0xff, 0xbf, 0x6d, 0x08,
115         0x7a, 0x6d, 0x08, 0x96, 0x74, 0x04, 0x00, 0x08,
116         0x79, 0xff, 0xda, 0xfe, 0xfe, 0xdb, 0xfe, 0xfe,
117         0x56, 0xff, 0xfe, 0x04, 0x00, 0x5e, 0xff, 0x5e,
118         0xff, 0x6c, 0xfe, 0xfe, 0xfe, 0x06, 0x00, 0x41,
119         0xff, 0x7f, 0x74, 0x03, 0x00, 0x11, 0x44, 0xff,
120         0xa9, 0xfe, 0xfe, 0xa9, 0xfe, 0xfe, 0xa5, 0x8f,
121         0x01, 0x00, 0x08, 0x01, 0x01, 0x02, 0x04, 0x08,
122         0x02, 0x04, 0x08, 0x08, 0x01, 0x01, 0xfe, 0x22,
123         0x00, 0x4c, 0x60, 0x64, 0x8c, 0x90, 0xd0, 0xd4,
124         0xd8, 0x5c, 0x10, 0x09, 0xd8, 0xff, 0xb0, 0xff,
125         0x00, 0x00, 0xba, 0xff, 0x14, 0x00, 0xba, 0xff,
126         0x64, 0x00, 0x00, 0x08, 0xfe, 0x06, 0x00, 0x74,
127         0xff, 0x42, 0xff, 0xce, 0xff, 0x60, 0xff, 0x0a,
128         0x00, 0xb4, 0x00, 0xa0, 0x00, 0xa0, 0xfe, 0x07,
129         0x00, 0x0a, 0x00, 0xb0, 0xff, 0x96, 0x4d, 0x00,
130         0x56, 0x57, 0x18, 0xa6, 0xff, 0x92, 0x70, 0x11,
131         0x00, 0x12, 0x90, 0x90, 0x76, 0x5a, 0x54, 0x54,
132         0x4c, 0x46, 0x38, 0x00, 0x10, 0x10, 0x08, 0xfe,
133         0x05, 0x00, 0x38, 0x29, 0x25, 0x23, 0x22, 0x22,
134         0x1f, 0x00, 0x00, 0x00, 0xf6, 0xe1, 0xdd, 0xf8,
135         0xfe, 0x00, 0xfe, 0x15, 0x00, 0x00, 0xd0, 0x02,
136         0x74, 0x02, 0x08, 0xf8, 0xe5, 0xde, 0x02, 0x04,
137         0x04, 0xfd, 0x00, 0x00, 0x00, 0x07, 0x50, 0x2d,
138         0x01, 0x90, 0x90, 0x76, 0x60, 0xb0, 0x07, 0x07,
139         0x0c, 0x0c, 0x04, 0xfe, 0x05, 0x00, 0x66, 0x66,
140         0x5a, 0x56, 0xbc, 0x01, 0x06, 0xfc, 0xfc, 0xf1,
141         0xfe, 0x07, 0x00, 0x24, 0x95, 0x70, 0x64, 0x18,
142         0x06, 0x2c, 0xff, 0xb5, 0xfe, 0xfe, 0xb5, 0xfe,
143         0xfe, 0xe2, 0x8c, 0x24, 0x02, 0x2f, 0xff, 0x2f,
144         0xff, 0xb4, 0x78, 0x02, 0x05, 0x73, 0xff, 0xed,
145         0xfe, 0xfe, 0x4f, 0xff, 0x36, 0x74, 0x1e, 0x09,
146         0x4f, 0xff, 0x50, 0xff, 0xfe, 0x16, 0x00, 0x70,
147         0xac, 0x70, 0x8e, 0xac, 0x40, 0x0e, 0x01, 0x70,
148         0x7f, 0x8e, 0xac, 0x6c, 0x00, 0x0b, 0xfe, 0x02,
149         0x00, 0xfe, 0x0a, 0x2c, 0x2a, 0x2a, 0x28, 0x26,
150         0x1e, 0x1e, 0xfe, 0x02, 0x20, 0x65, 0x20, 0x00,
151         0x00, 0x05, 0x12, 0x00, 0x11, 0x1e, 0x11, 0x11,
152         0x41, 0x1e, 0x41, 0x11, 0x31, 0x1e, 0x31, 0x11,
153         0x70, 0x75, 0x7a, 0x7f, 0x84, 0x89, 0x8e, 0x93,
154         0x98, 0x30, 0x20, 0x00, 0x02, 0x00, 0xfe, 0x06,
155         0x3c, 0xbc, 0x32, 0x0c, 0x00, 0x00, 0x2a, 0x12,
156         0x1e, 0x12, 0x2e, 0x12, 0xcc, 0x12, 0x11, 0x1a,
157         0x1e, 0x1a, 0x2e, 0x1a, 0x4c, 0x10, 0x1e, 0x10,
158         0x11, 0x18, 0x1e, 0x42, 0x1e, 0x42, 0x2e, 0x42,
159         0xcc, 0x42, 0x11, 0x4a, 0x1e, 0x4a, 0x2e, 0x4a,
160         0x4c, 0x40, 0x1e, 0x40, 0x11, 0x48, 0x1e, 0x32,
161         0x1e, 0x32, 0x2e, 0x32, 0xcc, 0x32, 0x11, 0x3a,
162         0x1e, 0x3a, 0x2e, 0x3a, 0x4c, 0x30, 0x1e, 0x30,
163         0x11, 0x38, 0x1e, 0x27, 0x9a, 0x01, 0x9d, 0xa2,
164         0x2f, 0x28, 0x00, 0x00, 0x46, 0xde, 0xc4, 0xbf,
165         0xa6, 0x9d, 0x81, 0x7b, 0x5c, 0x61, 0x40, 0xc7,
166         0xc0, 0xae, 0xa9, 0x8c, 0x83, 0x6a, 0x62, 0x50,
167         0x3e, 0xce, 0xc2, 0xae, 0xa3, 0x8c, 0x7b, 0x6a,
168         0x5a, 0x50, 0x35, 0xd7, 0xc2, 0xb7, 0xa4, 0x95,
169         0x7e, 0x72, 0x5a, 0x59, 0x37, 0xfe, 0x02, 0xf8,
170         0x8c, 0x95, 0x90, 0x8f, 0x00, 0xd7, 0xc0, 0xb7,
171         0xa2, 0x95, 0x7b, 0x72, 0x56, 0x59, 0x32, 0xc7,
172         0xc3, 0xae, 0xad, 0x8c, 0x85, 0x6a, 0x63, 0x50,
173         0x3e, 0xce, 0xc3, 0xae, 0xa4, 0x8c, 0x7c, 0x6a,
174         0x59, 0x50, 0x34, 0xd7, 0xc2, 0xb7, 0xa5, 0x95,
175         0x7e, 0x72, 0x59, 0x59, 0x36, 0xfc, 0x05, 0x00,
176         0x02, 0xce, 0xc5, 0xae, 0xa5, 0x95, 0x83, 0x72,
177         0x5c, 0x59, 0x36, 0xbf, 0xc6, 0xa5, 0xab, 0x8c,
178         0x8c, 0x6a, 0x67, 0x50, 0x41, 0x64, 0x07, 0x00,
179         0x02, 0x95, 0x8c, 0x72, 0x65, 0x59, 0x3f, 0xce,
180         0xc7, 0xae, 0xa8, 0x95, 0x86, 0x72, 0x5f, 0x59,
181         0x39, 0xfe, 0x02, 0xf8, 0x8b, 0x7c, 0x0b, 0x09,
182         0xb7, 0xc2, 0x9d, 0xa4, 0x83, 0x85, 0x6a, 0x6b,
183         0x50, 0x44, 0xb7, 0xc1, 0x64, 0x01, 0x00, 0x06,
184         0x61, 0x5d, 0x48, 0x3d, 0xae, 0xc4, 0x9d, 0xad,
185         0x7b, 0x85, 0x61, 0x66, 0x48, 0x46, 0xae, 0xc3,
186         0x95, 0xa3, 0x72, 0x7c, 0x59, 0x56, 0x38, 0x31,
187         0x7c, 0x0b, 0x00, 0x0c, 0x96, 0x91, 0x8f, 0x00,
188         0xb7, 0xc0, 0xa5, 0xab, 0x8c, 0x8a, 0x6a, 0x64,
189         0x50, 0x3c, 0xb7, 0xc0, 0x9d, 0xa0, 0x83, 0x80,
190         0x6a, 0x64, 0x50, 0x3d, 0xb7, 0xc5, 0x9d, 0xa5,
191         0x83, 0x87, 0x6c, 0x08, 0x07, 0xae, 0xc0, 0x9d,
192         0xa8, 0x83, 0x88, 0x6a, 0x6d, 0x50, 0x46, 0xfc,
193         0x05, 0x00, 0x16, 0xbf, 0xc0, 0xa5, 0xa2, 0x8c,
194         0x7f, 0x6a, 0x57, 0x50, 0x2f, 0xb7, 0xc7, 0xa5,
195         0xb1, 0x8c, 0x8e, 0x72, 0x6d, 0x59, 0x45, 0xbf,
196         0xc6, 0xa5, 0xa8, 0x8c, 0x87, 0x6a, 0x5f, 0x50,
197         0x37, 0xbf, 0xc2, 0xa5, 0xa4, 0x8c, 0x83, 0x6a,
198         0x5c, 0x50, 0x34, 0xbc, 0x05, 0x00, 0x0e, 0x90,
199         0x00, 0xc7, 0xc2, 0xae, 0xaa, 0x95, 0x82, 0x7b,
200         0x60, 0x61, 0x3f, 0xb7, 0xc6, 0xa5, 0xb1, 0x8c,
201         0x8d, 0x72, 0x6b, 0x61, 0x51, 0xbf, 0xc4, 0xa5,
202         0xa5, 0x8c, 0x82, 0x72, 0x61, 0x59, 0x39, 0x6c,
203         0x26, 0x03, 0x95, 0x82, 0x7b, 0x61, 0x61, 0x40,
204         0xfc, 0x05, 0x00, 0x00, 0x7e, 0xd7, 0xc3, 0xb7,
205         0xa8, 0x9d, 0x80, 0x83, 0x5d, 0x6a, 0x3f, 0xbf,
206         0xc7, 0xa5, 0xa8, 0x8c, 0x84, 0x72, 0x60, 0x61,
207         0x46, 0xbf, 0xc2, 0xae, 0xb0, 0x9d, 0x92, 0x83,
208         0x6f, 0x6a, 0x50, 0xd7, 0xc3, 0xb7, 0xa7, 0x9d,
209         0x80, 0x83, 0x5e, 0x6a, 0x40, 0xfe, 0x02, 0xf8,
210         0x8d, 0x96, 0x90, 0x90, 0xfe, 0x05, 0x00, 0x8a,
211         0xc4, 0x63, 0xb8, 0x3c, 0xa6, 0x29, 0x97, 0x16,
212         0x81, 0x84, 0xb7, 0x5b, 0xa9, 0x33, 0x94, 0x1e,
213         0x83, 0x11, 0x70, 0xb8, 0xc2, 0x70, 0xb1, 0x4d,
214         0xa3, 0x2a, 0x8d, 0x1b, 0x7b, 0xa8, 0xbc, 0x68,
215         0xab, 0x47, 0x9d, 0x27, 0x87, 0x18, 0x75, 0xae,
216         0xc6, 0x7d, 0xbb, 0x4d, 0xaa, 0x1c, 0x84, 0x11,
217         0x72, 0xa3, 0xbb, 0x6e, 0xad, 0x3c, 0x97, 0x24,
218         0x85, 0x16, 0x71, 0x80, 0xb2, 0x57, 0xa4, 0x30,
219         0x8e, 0x1c, 0x7c, 0x10, 0x68, 0xbb, 0xbd, 0x75,
220         0xac, 0x4f, 0x9e, 0x2b, 0x87, 0x1a, 0x76, 0x96,
221         0xc5, 0x5e, 0xb5, 0x3e, 0xa5, 0x1f, 0x8c, 0x12,
222         0x7a, 0xc1, 0xc6, 0x42, 0x9f, 0x27, 0x8c, 0x16,
223         0x77, 0x0f, 0x67, 0x9d, 0xbc, 0x68, 0xad, 0x36,
224         0x95, 0x20, 0x83, 0x11, 0x6d, 0x9b, 0xb8, 0x67,
225         0xa8, 0x34, 0x90, 0x1f, 0x7c, 0x10, 0x67, 0x9e,
226         0xc9, 0x6a, 0xbb, 0x37, 0xa4, 0x20, 0x90, 0x11,
227         0x7b, 0xc6, 0xc8, 0x47, 0xa4, 0x2a, 0x90, 0x18,
228         0x7b, 0x10, 0x6c, 0xae, 0xc4, 0x5d, 0xad, 0x37,
229         0x9a, 0x1f, 0x85, 0x13, 0x75, 0x70, 0xad, 0x42,
230         0x99, 0x25, 0x84, 0x17, 0x74, 0x0b, 0x56, 0x87,
231         0xc8, 0x57, 0xb8, 0x2b, 0x9e, 0x19, 0x8a, 0x0d,
232         0x74, 0xa7, 0xc8, 0x6e, 0xb9, 0x36, 0xa0, 0x1f,
233         0x8b, 0x11, 0x75, 0x94, 0xbe, 0x4b, 0xa5, 0x2a,
234         0x92, 0x18, 0x7c, 0x0f, 0x6b, 0xaf, 0xc0, 0x58,
235         0xa8, 0x34, 0x94, 0x1d, 0x7d, 0x12, 0x6d, 0x82,
236         0xc0, 0x52, 0xb0, 0x25, 0x94, 0x14, 0x7f, 0x0c,
237         0x68, 0x84, 0xbf, 0x3e, 0xa4, 0x22, 0x8e, 0x10,
238         0x76, 0x0b, 0x65, 0x88, 0xb6, 0x42, 0x9b, 0x26,
239         0x87, 0x14, 0x70, 0x0c, 0x5f, 0xc5, 0xc2, 0x3e,
240         0x97, 0x23, 0x83, 0x13, 0x6c, 0x0c, 0x5c, 0xb1,
241         0xc9, 0x76, 0xbc, 0x4a, 0xaa, 0x20, 0x8d, 0x12,
242         0x78, 0x93, 0xbf, 0x46, 0xa3, 0x26, 0x8d, 0x14,
243         0x74, 0x0c, 0x62, 0xc8, 0xc4, 0x3b, 0x97, 0x21,
244         0x82, 0x11, 0x6a, 0x0a, 0x59, 0xa3, 0xb9, 0x68,
245         0xa9, 0x30, 0x8d, 0x1a, 0x78, 0x0f, 0x61, 0xa0,
246         0xc9, 0x73, 0xbe, 0x50, 0xb1, 0x30, 0x9f, 0x14,
247         0x80, 0x83, 0xb7, 0x3c, 0x9a, 0x20, 0x84, 0x0e,
248         0x6a, 0x0a, 0x57, 0xac, 0xc2, 0x68, 0xb0, 0x2e,
249         0x92, 0x19, 0x7c, 0x0d, 0x63, 0x93, 0xbe, 0x62,
250         0xb0, 0x3c, 0x9e, 0x1a, 0x80, 0x0e, 0x6b, 0xbb,
251         0x02, 0xa0, 0x02, 0xa0, 0x02, 0x6f, 0x00, 0x75,
252         0x00, 0x75, 0x00, 0x00, 0x00, 0xad, 0x02, 0xb3,
253         0x02, 0x6f, 0x00, 0x87, 0x00, 0x85, 0xfe, 0x03,
254         0x00, 0xc2, 0x02, 0x82, 0x4d, 0x92, 0x6e, 0x4d,
255         0xb1, 0xa8, 0x84, 0x01, 0x00, 0x07, 0x7e, 0x00,
256         0xa8, 0x02, 0xa4, 0x02, 0xa4, 0x02, 0xa2, 0x00,
257         0xa6, 0x00, 0xa6, 0x00, 0x00, 0x00, 0xb4, 0x02,
258         0xb4, 0x02, 0x92, 0x00, 0x96, 0x00, 0x96, 0x46,
259         0x04, 0xb0, 0x02, 0x64, 0x02, 0x0a, 0x8c, 0x00,
260         0x90, 0x02, 0x98, 0x02, 0x98, 0x02, 0x0e, 0x01,
261         0x11, 0x01, 0x11, 0x50, 0xc3, 0x08, 0x88, 0x02,
262         0x88, 0x02, 0x19, 0x01, 0x02, 0x01, 0x02, 0x01,
263         0xf3, 0x2d, 0x00, 0x00
264 };
265
266 /* Array of known hw_options bits with human-friendly parsing */
267 static struct hc_hwopt {
268         const u32 bit;
269         const char *str;
270 } const hc_hwopts[] = {
271         {
272                 .bit = RB_HW_OPT_NO_UART,
273                 .str = "no UART\t\t",
274         }, {
275                 .bit = RB_HW_OPT_HAS_VOLTAGE,
276                 .str = "has Vreg\t",
277         }, {
278                 .bit = RB_HW_OPT_HAS_USB,
279                 .str = "has usb\t\t",
280         }, {
281                 .bit = RB_HW_OPT_HAS_ATTINY,
282                 .str = "has ATtiny\t",
283         }, {
284                 .bit = RB_HW_OPT_NO_NAND,
285                 .str = "no NAND\t\t",
286         }, {
287                 .bit = RB_HW_OPT_HAS_LCD,
288                 .str = "has LCD\t\t",
289         }, {
290                 .bit = RB_HW_OPT_HAS_POE_OUT,
291                 .str = "has POE out\t",
292         }, {
293                 .bit = RB_HW_OPT_HAS_uSD,
294                 .str = "has MicroSD\t",
295         }, {
296                 .bit = RB_HW_OPT_HAS_SIM,
297                 .str = "has SIM\t\t",
298         }, {
299                 .bit = RB_HW_OPT_HAS_SFP,
300                 .str = "has SFP\t\t",
301         }, {
302                 .bit = RB_HW_OPT_HAS_WIFI,
303                 .str = "has WiFi\t",
304         }, {
305                 .bit = RB_HW_OPT_HAS_TS_FOR_ADC,
306                 .str = "has TS ADC\t",
307         }, {
308                 .bit = RB_HW_OPT_HAS_PLC,
309                 .str = "has PLC\t\t",
310         },
311 };
312
313 static ssize_t hc_tag_show_string(const u8 *pld, u16 pld_len, char *buf)
314 {
315         return snprintf(buf, pld_len+1, "%s\n", pld);
316 }
317
318 static ssize_t hc_tag_show_u32(const u8 *pld, u16 pld_len, char *buf)
319 {
320         char *out = buf;
321         u32 data;       // cpu-endian
322
323         /* Caller ensures pld_len > 0 */
324         if (pld_len % sizeof(data))
325                 return -EINVAL;
326
327         data = *(u32 *)pld;
328
329         do {
330                 out += sprintf(out, "0x%08x\n", data);
331                 data++;
332         } while ((pld_len -= sizeof(data)));
333
334         return out - buf;
335 }
336
337 /*
338  * The MAC is stored network-endian on all devices, in 2 32-bit segments:
339  * <XX:XX:XX:XX> <XX:XX:00:00>. Kernel print has us covered.
340  */
341 static ssize_t hc_tag_show_mac(const u8 *pld, u16 pld_len, char *buf)
342 {
343         if (8 != pld_len)
344                 return -EINVAL;
345
346         return sprintf(buf, "%pM\n", pld);
347 }
348
349 /*
350  * Print HW options in a human readable way:
351  * The raw number and in decoded form
352  */
353 static ssize_t hc_tag_show_hwoptions(const u8 *pld, u16 pld_len, char *buf)
354 {
355         char *out = buf;
356         u32 data;       // cpu-endian
357         int i;
358
359         if (sizeof(data) != pld_len)
360                 return -EINVAL;
361
362         data = *(u32 *)pld;
363         out += sprintf(out, "raw\t\t: 0x%08x\n\n", data);
364
365         for (i = 0; i < ARRAY_SIZE(hc_hwopts); i++)
366                 out += sprintf(out, "%s: %s\n", hc_hwopts[i].str,
367                                (data & hc_hwopts[i].bit) ? "true" : "false");
368
369         return out - buf;
370 }
371
372 static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj,
373                                      struct bin_attribute *attr, char *buf,
374                                      loff_t off, size_t count);
375
376 static struct hc_wlan_attr {
377         struct bin_attribute battr;
378         u16 pld_ofs;
379         u16 pld_len;
380 } hc_wlandata_battr = {
381         .battr = __BIN_ATTR(wlan_data, S_IRUSR, hc_wlan_data_bin_read, NULL, 0),
382 };
383
384 static ssize_t hc_attr_show(struct kobject *kobj, struct kobj_attribute *attr,
385                             char *buf);
386
387 /* Array of known tags to publish in sysfs */
388 static struct hc_attr {
389         const u16 tag_id;
390         ssize_t (* const tshow)(const u8 *pld, u16 pld_len, char *buf);
391         struct kobj_attribute kattr;
392         u16 pld_ofs;
393         u16 pld_len;
394 } hc_attrs[] = {
395         {
396                 .tag_id = RB_ID_FLASH_INFO,
397                 .tshow = hc_tag_show_u32,
398                 .kattr = __ATTR(flash_info, S_IRUSR, hc_attr_show, NULL),
399         }, {
400                 .tag_id = RB_ID_MAC_ADDRESS_PACK,
401                 .tshow = hc_tag_show_mac,
402                 .kattr = __ATTR(mac_base, S_IRUSR, hc_attr_show, NULL),
403         }, {
404                 .tag_id = RB_ID_BOARD_PRODUCT_CODE,
405                 .tshow = hc_tag_show_string,
406                 .kattr = __ATTR(board_product_code, S_IRUSR, hc_attr_show, NULL),
407         }, {
408                 .tag_id = RB_ID_BIOS_VERSION,
409                 .tshow = hc_tag_show_string,
410                 .kattr = __ATTR(booter_version, S_IRUSR, hc_attr_show, NULL),
411         }, {
412                 .tag_id = RB_ID_SERIAL_NUMBER,
413                 .tshow = hc_tag_show_string,
414                 .kattr = __ATTR(board_serial, S_IRUSR, hc_attr_show, NULL),
415         }, {
416                 .tag_id = RB_ID_MEMORY_SIZE,
417                 .tshow = hc_tag_show_u32,
418                 .kattr = __ATTR(mem_size, S_IRUSR, hc_attr_show, NULL),
419         }, {
420                 .tag_id = RB_ID_MAC_ADDRESS_COUNT,
421                 .tshow = hc_tag_show_u32,
422                 .kattr = __ATTR(mac_count, S_IRUSR, hc_attr_show, NULL),
423         }, {
424                 .tag_id = RB_ID_HW_OPTIONS,
425                 .tshow = hc_tag_show_hwoptions,
426                 .kattr = __ATTR(hw_options, S_IRUSR, hc_attr_show, NULL),
427         }, {
428                 .tag_id = RB_ID_WLAN_DATA,
429                 .tshow = NULL,
430         }, {
431                 .tag_id = RB_ID_BOARD_IDENTIFIER,
432                 .tshow = hc_tag_show_string,
433                 .kattr = __ATTR(board_identifier, S_IRUSR, hc_attr_show, NULL),
434         }, {
435                 .tag_id = RB_ID_PRODUCT_NAME,
436                 .tshow = hc_tag_show_string,
437                 .kattr = __ATTR(product_name, S_IRUSR, hc_attr_show, NULL),
438         }, {
439                 .tag_id = RB_ID_DEFCONF,
440                 .tshow = hc_tag_show_string,
441                 .kattr = __ATTR(defconf, S_IRUSR, hc_attr_show, NULL),
442         }
443 };
444
445 /*
446  * If the RB_ID_WLAN_DATA payload starts with RB_MAGIC_ERD, then past
447  * that magic number the payload itself contains a routerboot tag node
448  * locating the LZO-compressed calibration data at id 0x1.
449  */
450 static int hc_wlan_data_unpack_erd(const u8 *inbuf, size_t inlen,
451                                    void *outbuf, size_t *outlen)
452 {
453         u16 lzo_ofs, lzo_len;
454         int ret;
455
456         /* Find embedded tag */
457         ret = routerboot_tag_find(inbuf, inlen, 0x1,    // always id 1
458                                   &lzo_ofs, &lzo_len);
459         if (ret) {
460                 pr_debug(RB_HC_PR_PFX "ERD data not found\n");
461                 goto fail;
462         }
463
464         if (lzo_len > inlen) {
465                 pr_debug(RB_HC_PR_PFX "Invalid ERD data length\n");
466                 ret = -EINVAL;
467                 goto fail;
468         }
469
470         ret = lzo1x_decompress_safe(inbuf+lzo_ofs, lzo_len, outbuf, outlen);
471         if (ret)
472                 pr_debug(RB_HC_PR_PFX "LZO decompression error (%d)\n", ret);
473
474 fail:
475         return ret;
476 }
477
478 /*
479  * If the RB_ID_WLAN_DATA payload starts with RB_MAGIC_LZOR, then past
480  * that magic number is a payload that must be appended to the hc_lzor_prefix,
481  * the resulting blob is LZO-compressed. In the LZO decompression result,
482  * the RB_MAGIC_ERD magic number (aligned) must be located. Following that
483  * magic, there is a routerboot tag node (id 0x1) locating the RLE-encoded
484  * calibration data payload.
485  */
486 static int hc_wlan_data_unpack_lzor(const u8 *inbuf, size_t inlen,
487                                     void *outbuf, size_t *outlen)
488 {
489         u16 rle_ofs, rle_len;
490         const u32 *needle;
491         u8 *tempbuf;
492         size_t templen, lzo_len;
493         int ret;
494
495         lzo_len = inlen + sizeof(hc_lzor_prefix);
496         if (lzo_len > *outlen)
497                 return -EFBIG;
498
499         /* Temporary buffer same size as the outbuf */
500         templen = *outlen;
501         tempbuf = kmalloc(templen, GFP_KERNEL);
502         if (!outbuf)
503                 return -ENOMEM;
504
505         /* Concatenate into the outbuf */
506         memcpy(outbuf, hc_lzor_prefix, sizeof(hc_lzor_prefix));
507         memcpy(outbuf + sizeof(hc_lzor_prefix), inbuf, inlen);
508
509         /* LZO-decompress lzo_len bytes of outbuf into the tempbuf */
510         ret = lzo1x_decompress_safe(outbuf, lzo_len, tempbuf, &templen);
511         if (ret) {
512                 if (LZO_E_INPUT_NOT_CONSUMED == ret) {
513                         /*
514                          * The tag length appears to always be aligned (probably
515                          * because it is the "root" RB_ID_WLAN_DATA tag), thus
516                          * the LZO payload may be padded, which can trigger a
517                          * spurious error which we ignore here.
518                          */
519                         pr_debug(RB_HC_PR_PFX "LZOR: LZO EOF before buffer end - this may be harmless\n");
520                 } else {
521                         pr_debug(RB_HC_PR_PFX "LZOR: LZO decompression error (%d)\n", ret);
522                         goto fail;
523                 }
524         }
525
526         /*
527          * Post decompression we have a blob (possibly byproduct of the lzo
528          * dictionary). We need to find RB_MAGIC_ERD. The magic number seems to
529          * be 32bit-aligned in the decompression output.
530          */
531         needle = (const u32 *)tempbuf;
532         while (RB_MAGIC_ERD != *needle++) {
533                 if ((u8 *)needle >= tempbuf+templen) {
534                         pr_debug(RB_HC_PR_PFX "LZOR: ERD magic not found\n");
535                         ret = -ENODATA;
536                         goto fail;
537                 }
538         };
539         templen -= (u8 *)needle - tempbuf;
540
541         /* Past magic. Look for tag node */
542         ret = routerboot_tag_find((u8 *)needle, templen, 0x1, &rle_ofs, &rle_len);
543         if (ret) {
544                 pr_debug(RB_HC_PR_PFX "LZOR: RLE data not found\n");
545                 goto fail;
546         }
547
548         if (rle_len > templen) {
549                 pr_debug(RB_HC_PR_PFX "LZOR: Invalid RLE data length\n");
550                 ret = -EINVAL;
551                 goto fail;
552         }
553
554         /* RLE-decode tempbuf from needle back into the outbuf */
555         ret = routerboot_rle_decode((u8 *)needle+rle_ofs, rle_len, outbuf, outlen);
556         if (ret)
557                 pr_debug(RB_HC_PR_PFX "LZOR: RLE decoding error (%d)\n", ret);
558
559 fail:
560         kfree(tempbuf);
561         return ret;
562 }
563
564 static int hc_wlan_data_unpack(const size_t tofs, size_t tlen,
565                                void *outbuf, size_t *outlen)
566 {
567         const u8 *lbuf;
568         u32 magic;
569         int ret;
570
571         /* Caller ensure tlen > 0. tofs is aligned */
572         if ((tofs + tlen) > hc_buflen)
573                 return -EIO;
574
575         lbuf = hc_buf + tofs;
576         magic = *(u32 *)lbuf;
577
578         ret = -ENODATA;
579         switch (magic) {
580         case RB_MAGIC_LZOR:
581                 /* Skip magic */
582                 lbuf += sizeof(magic);
583                 tlen -= sizeof(magic);
584                 ret = hc_wlan_data_unpack_lzor(lbuf, tlen, outbuf, outlen);
585                 break;
586         case RB_MAGIC_ERD:
587                 /* Skip magic */
588                 lbuf += sizeof(magic);
589                 tlen -= sizeof(magic);
590                 ret = hc_wlan_data_unpack_erd(lbuf, tlen, outbuf, outlen);
591                 break;
592         default:
593                 /*
594                  * If the RB_ID_WLAN_DATA payload doesn't start with a
595                  * magic number, the payload itself is the raw RLE-encoded
596                  * calibration data.
597                  */
598                 ret = routerboot_rle_decode(lbuf, tlen, outbuf, outlen);
599                 if (ret)
600                         pr_debug(RB_HC_PR_PFX "RLE decoding error (%d)\n", ret);
601                 break;
602         }
603
604         return ret;
605 }
606
607 static ssize_t hc_attr_show(struct kobject *kobj, struct kobj_attribute *attr,
608                             char *buf)
609 {
610         const struct hc_attr *hc_attr;
611         const u8 *pld;
612         u16 pld_len;
613
614         hc_attr = container_of(attr, typeof(*hc_attr), kattr);
615
616         if (!hc_attr->pld_len)
617                 return -ENOENT;
618
619         pld = hc_buf + hc_attr->pld_ofs;
620         pld_len = hc_attr->pld_len;
621
622         return hc_attr->tshow(pld, pld_len, buf);
623 }
624
625 /*
626  * This function will allocate and free memory every time it is called. This
627  * is not the fastest way to do this, but since the data is rarely read (mainly
628  * at boot time to load wlan caldata), this makes it possible to save memory for
629  * the system.
630  */
631 static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj,
632                                      struct bin_attribute *attr, char *buf,
633                                      loff_t off, size_t count)
634 {
635         struct hc_wlan_attr *hc_wattr;
636         size_t outlen;
637         void *outbuf;
638         int ret;
639
640         hc_wattr = container_of(attr, typeof(*hc_wattr), battr);
641
642         if (!hc_wattr->pld_len)
643                 return -ENOENT;
644
645         outlen = RB_ART_SIZE;
646
647         /* Don't bother unpacking if the source is already too large */
648         if (hc_wattr->pld_len > outlen)
649                 return -EFBIG;
650
651         outbuf = kmalloc(outlen, GFP_KERNEL);
652         if (!outbuf)
653                 return -ENOMEM;
654
655         ret = hc_wlan_data_unpack(hc_wattr->pld_ofs, hc_wattr->pld_len, outbuf, &outlen);
656         if (ret) {
657                 kfree(outbuf);
658                 return ret;
659         }
660
661         if (off >= outlen) {
662                 kfree(outbuf);
663                 return 0;
664         }
665
666         if (off + count > outlen)
667                 count = outlen - off;
668
669         memcpy(buf, outbuf + off, count);
670
671         kfree(outbuf);
672         return count;
673 }
674
675 int __init rb_hardconfig_init(struct kobject *rb_kobj)
676 {
677         struct mtd_info *mtd;
678         size_t bytes_read, buflen;
679         const u8 *buf;
680         int i, ret;
681         u32 magic;
682
683         // TODO allow override
684         mtd = get_mtd_device_nm(RB_MTD_HARD_CONFIG);
685         if (IS_ERR(mtd))
686                 return -ENODEV;
687
688         hc_buflen = mtd->size;
689         hc_buf = kmalloc(hc_buflen, GFP_KERNEL);
690         if (!hc_buf)
691                 return -ENOMEM;
692
693         ret = mtd_read(mtd, 0, hc_buflen, &bytes_read, hc_buf);
694
695         if (ret)
696                 goto fail;
697
698         if (bytes_read != hc_buflen) {
699                 ret = -EIO;
700                 goto fail;
701         }
702
703         /* Check we have what we expect */
704         magic = *(const u32 *)hc_buf;
705         if (RB_MAGIC_HARD != magic) {
706                 ret = -EINVAL;
707                 goto fail;
708         }
709
710         /* Skip magic */
711         buf = hc_buf + sizeof(magic);
712         buflen = hc_buflen - sizeof(magic);
713
714         /* Populate sysfs */
715         ret = -ENOMEM;
716         hc_kobj = kobject_create_and_add(RB_MTD_HARD_CONFIG, rb_kobj);
717         if (!hc_kobj)
718                 goto fail;
719
720         /* Locate and publish all known tags */
721         for (i = 0; i < ARRAY_SIZE(hc_attrs); i++) {
722                 ret = routerboot_tag_find(buf, buflen, hc_attrs[i].tag_id,
723                                           &hc_attrs[i].pld_ofs, &hc_attrs[i].pld_len);
724                 if (ret) {
725                         hc_attrs[i].pld_ofs = hc_attrs[i].pld_len = 0;
726                         continue;
727                 }
728
729                 /* Account for skipped magic */
730                 hc_attrs[i].pld_ofs += sizeof(magic);
731
732                 /* Special case RB_ID_WLAN_DATA to prep and create the binary attribute */
733                 if ((RB_ID_WLAN_DATA == hc_attrs[i].tag_id) && hc_attrs[i].pld_len) {
734                         hc_wlandata_battr.pld_ofs = hc_attrs[i].pld_ofs;
735                         hc_wlandata_battr.pld_len = hc_attrs[i].pld_len;
736
737                         ret = sysfs_create_bin_file(hc_kobj, &hc_wlandata_battr.battr);
738                         if (ret)
739                                 pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n",
740                                        hc_wlandata_battr.battr.attr.name, ret);
741                 }
742                 /* All other tags are published via standard attributes */
743                 else {
744                         ret = sysfs_create_file(hc_kobj, &hc_attrs[i].kattr.attr);
745                         if (ret)
746                                 pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n",
747                                        hc_attrs[i].kattr.attr.name, ret);
748                 }
749         }
750
751         pr_info("MikroTik RouterBOARD hardware configuration sysfs driver v" RB_HARDCONFIG_VER "\n");
752
753         return 0;
754
755 fail:
756         kfree(hc_buf);
757         return ret;
758 }
759
760 void __exit rb_hardconfig_exit(void)
761 {
762         kobject_put(hc_kobj);
763         kfree(hc_buf);
764 }