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