1 // SPDX-License-Identifier: GPL-2.0-only
3 * Driver for MikroTik RouterBoot hard config.
5 * Copyright (C) 2020 Thibaut VARĂˆNE <hacks+kernel@slashdirt.org>
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.
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.
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.
21 * Note: PAGE_SIZE is assumed to be >= 4K, hence the device attribute show
22 * routines need not check for output overflow.
24 * Some constant defines extracted from routerboot.{c,h} by Gabor Juhos
25 * <juhosg@openwrt.org>
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>
40 #include "routerboot.h"
42 #define RB_HARDCONFIG_VER "0.04"
43 #define RB_HC_PR_PFX "[rb_hardconfig] "
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
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)
78 static struct kobject *hc_kobj;
79 static u8 *hc_buf; // ro buffer after init(): no locking required
80 static size_t hc_buflen;
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
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
266 /* Array of known hw_options bits with human-friendly parsing */
267 static struct hc_hwopt {
270 } const hc_hwopts[] = {
272 .bit = RB_HW_OPT_NO_UART,
273 .str = "no UART\t\t",
275 .bit = RB_HW_OPT_HAS_VOLTAGE,
278 .bit = RB_HW_OPT_HAS_USB,
279 .str = "has usb\t\t",
281 .bit = RB_HW_OPT_HAS_ATTINY,
282 .str = "has ATtiny\t",
284 .bit = RB_HW_OPT_NO_NAND,
285 .str = "no NAND\t\t",
287 .bit = RB_HW_OPT_HAS_LCD,
288 .str = "has LCD\t\t",
290 .bit = RB_HW_OPT_HAS_POE_OUT,
291 .str = "has POE out\t",
293 .bit = RB_HW_OPT_HAS_uSD,
294 .str = "has MicroSD\t",
296 .bit = RB_HW_OPT_HAS_SIM,
297 .str = "has SIM\t\t",
299 .bit = RB_HW_OPT_HAS_SFP,
300 .str = "has SFP\t\t",
302 .bit = RB_HW_OPT_HAS_WIFI,
305 .bit = RB_HW_OPT_HAS_TS_FOR_ADC,
306 .str = "has TS ADC\t",
308 .bit = RB_HW_OPT_HAS_PLC,
309 .str = "has PLC\t\t",
313 static ssize_t hc_tag_show_string(const u8 *pld, u16 pld_len, char *buf)
315 return snprintf(buf, pld_len+1, "%s\n", pld);
318 static ssize_t hc_tag_show_u32(const u8 *pld, u16 pld_len, char *buf)
321 u32 data; // cpu-endian
323 /* Caller ensures pld_len > 0 */
324 if (pld_len % sizeof(data))
330 out += sprintf(out, "0x%08x\n", data);
332 } while ((pld_len -= sizeof(data)));
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.
341 static ssize_t hc_tag_show_mac(const u8 *pld, u16 pld_len, char *buf)
346 return sprintf(buf, "%pM\n", pld);
350 * Print HW options in a human readable way:
351 * The raw number and in decoded form
353 static ssize_t hc_tag_show_hwoptions(const u8 *pld, u16 pld_len, char *buf)
356 u32 data; // cpu-endian
359 if (sizeof(data) != pld_len)
363 out += sprintf(out, "raw\t\t: 0x%08x\n\n", data);
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");
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);
376 static struct hc_wlan_attr {
377 struct bin_attribute battr;
380 } hc_wlandata_battr = {
381 .battr = __BIN_ATTR(wlan_data, S_IRUSR, hc_wlan_data_bin_read, NULL, 0),
384 static ssize_t hc_attr_show(struct kobject *kobj, struct kobj_attribute *attr,
387 /* Array of known tags to publish in sysfs */
388 static struct hc_attr {
390 ssize_t (* const tshow)(const u8 *pld, u16 pld_len, char *buf);
391 struct kobj_attribute kattr;
396 .tag_id = RB_ID_FLASH_INFO,
397 .tshow = hc_tag_show_u32,
398 .kattr = __ATTR(flash_info, S_IRUSR, hc_attr_show, NULL),
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),
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),
408 .tag_id = RB_ID_BIOS_VERSION,
409 .tshow = hc_tag_show_string,
410 .kattr = __ATTR(booter_version, S_IRUSR, hc_attr_show, NULL),
412 .tag_id = RB_ID_SERIAL_NUMBER,
413 .tshow = hc_tag_show_string,
414 .kattr = __ATTR(board_serial, S_IRUSR, hc_attr_show, NULL),
416 .tag_id = RB_ID_MEMORY_SIZE,
417 .tshow = hc_tag_show_u32,
418 .kattr = __ATTR(mem_size, S_IRUSR, hc_attr_show, NULL),
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),
424 .tag_id = RB_ID_HW_OPTIONS,
425 .tshow = hc_tag_show_hwoptions,
426 .kattr = __ATTR(hw_options, S_IRUSR, hc_attr_show, NULL),
428 .tag_id = RB_ID_WLAN_DATA,
431 .tag_id = RB_ID_BOARD_IDENTIFIER,
432 .tshow = hc_tag_show_string,
433 .kattr = __ATTR(board_identifier, S_IRUSR, hc_attr_show, NULL),
435 .tag_id = RB_ID_PRODUCT_NAME,
436 .tshow = hc_tag_show_string,
437 .kattr = __ATTR(product_name, S_IRUSR, hc_attr_show, NULL),
439 .tag_id = RB_ID_DEFCONF,
440 .tshow = hc_tag_show_string,
441 .kattr = __ATTR(defconf, S_IRUSR, hc_attr_show, NULL),
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.
450 static int hc_wlan_data_unpack_erd(const u8 *inbuf, size_t inlen,
451 void *outbuf, size_t *outlen)
453 u16 lzo_ofs, lzo_len;
456 /* Find embedded tag */
457 ret = routerboot_tag_find(inbuf, inlen, 0x1, // always id 1
460 pr_debug(RB_HC_PR_PFX "ERD data not found\n");
464 if (lzo_len > inlen) {
465 pr_debug(RB_HC_PR_PFX "Invalid ERD data length\n");
470 ret = lzo1x_decompress_safe(inbuf+lzo_ofs, lzo_len, outbuf, outlen);
472 pr_debug(RB_HC_PR_PFX "LZO decompression error (%d)\n", ret);
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.
486 static int hc_wlan_data_unpack_lzor(const u8 *inbuf, size_t inlen,
487 void *outbuf, size_t *outlen)
489 u16 rle_ofs, rle_len;
492 size_t templen, lzo_len;
495 lzo_len = inlen + sizeof(hc_lzor_prefix);
496 if (lzo_len > *outlen)
499 /* Temporary buffer same size as the outbuf */
501 tempbuf = kmalloc(templen, GFP_KERNEL);
505 /* Concatenate into the outbuf */
506 memcpy(outbuf, hc_lzor_prefix, sizeof(hc_lzor_prefix));
507 memcpy(outbuf + sizeof(hc_lzor_prefix), inbuf, inlen);
509 /* LZO-decompress lzo_len bytes of outbuf into the tempbuf */
510 ret = lzo1x_decompress_safe(outbuf, lzo_len, tempbuf, &templen);
512 if (LZO_E_INPUT_NOT_CONSUMED == ret) {
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.
519 pr_debug(RB_HC_PR_PFX "LZOR: LZO EOF before buffer end - this may be harmless\n");
521 pr_debug(RB_HC_PR_PFX "LZOR: LZO decompression error (%d)\n", ret);
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.
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");
539 templen -= (u8 *)needle - tempbuf;
541 /* Past magic. Look for tag node */
542 ret = routerboot_tag_find((u8 *)needle, templen, 0x1, &rle_ofs, &rle_len);
544 pr_debug(RB_HC_PR_PFX "LZOR: RLE data not found\n");
548 if (rle_len > templen) {
549 pr_debug(RB_HC_PR_PFX "LZOR: Invalid RLE data length\n");
554 /* RLE-decode tempbuf from needle back into the outbuf */
555 ret = routerboot_rle_decode((u8 *)needle+rle_ofs, rle_len, outbuf, outlen);
557 pr_debug(RB_HC_PR_PFX "LZOR: RLE decoding error (%d)\n", ret);
564 static int hc_wlan_data_unpack(const size_t tofs, size_t tlen,
565 void *outbuf, size_t *outlen)
571 /* Caller ensure tlen > 0. tofs is aligned */
572 if ((tofs + tlen) > hc_buflen)
575 lbuf = hc_buf + tofs;
576 magic = *(u32 *)lbuf;
582 lbuf += sizeof(magic);
583 tlen -= sizeof(magic);
584 ret = hc_wlan_data_unpack_lzor(lbuf, tlen, outbuf, outlen);
588 lbuf += sizeof(magic);
589 tlen -= sizeof(magic);
590 ret = hc_wlan_data_unpack_erd(lbuf, tlen, outbuf, outlen);
594 * If the RB_ID_WLAN_DATA payload doesn't start with a
595 * magic number, the payload itself is the raw RLE-encoded
598 ret = routerboot_rle_decode(lbuf, tlen, outbuf, outlen);
600 pr_debug(RB_HC_PR_PFX "RLE decoding error (%d)\n", ret);
607 static ssize_t hc_attr_show(struct kobject *kobj, struct kobj_attribute *attr,
610 const struct hc_attr *hc_attr;
614 hc_attr = container_of(attr, typeof(*hc_attr), kattr);
616 if (!hc_attr->pld_len)
619 pld = hc_buf + hc_attr->pld_ofs;
620 pld_len = hc_attr->pld_len;
622 return hc_attr->tshow(pld, pld_len, buf);
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
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)
635 struct hc_wlan_attr *hc_wattr;
640 hc_wattr = container_of(attr, typeof(*hc_wattr), battr);
642 if (!hc_wattr->pld_len)
645 outlen = RB_ART_SIZE;
647 /* Don't bother unpacking if the source is already too large */
648 if (hc_wattr->pld_len > outlen)
651 outbuf = kmalloc(outlen, GFP_KERNEL);
655 ret = hc_wlan_data_unpack(hc_wattr->pld_ofs, hc_wattr->pld_len, outbuf, &outlen);
666 if (off + count > outlen)
667 count = outlen - off;
669 memcpy(buf, outbuf + off, count);
675 int __init rb_hardconfig_init(struct kobject *rb_kobj)
677 struct mtd_info *mtd;
678 size_t bytes_read, buflen;
683 // TODO allow override
684 mtd = get_mtd_device_nm(RB_MTD_HARD_CONFIG);
688 hc_buflen = mtd->size;
689 hc_buf = kmalloc(hc_buflen, GFP_KERNEL);
693 ret = mtd_read(mtd, 0, hc_buflen, &bytes_read, hc_buf);
698 if (bytes_read != hc_buflen) {
703 /* Check we have what we expect */
704 magic = *(const u32 *)hc_buf;
705 if (RB_MAGIC_HARD != magic) {
711 buf = hc_buf + sizeof(magic);
712 buflen = hc_buflen - sizeof(magic);
716 hc_kobj = kobject_create_and_add(RB_MTD_HARD_CONFIG, rb_kobj);
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);
725 hc_attrs[i].pld_ofs = hc_attrs[i].pld_len = 0;
729 /* Account for skipped magic */
730 hc_attrs[i].pld_ofs += sizeof(magic);
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;
737 ret = sysfs_create_bin_file(hc_kobj, &hc_wlandata_battr.battr);
739 pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n",
740 hc_wlandata_battr.battr.attr.name, ret);
742 /* All other tags are published via standard attributes */
744 ret = sysfs_create_file(hc_kobj, &hc_attrs[i].kattr.attr);
746 pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n",
747 hc_attrs[i].kattr.attr.name, ret);
751 pr_info("MikroTik RouterBOARD hardware configuration sysfs driver v" RB_HARDCONFIG_VER "\n");
760 void __exit rb_hardconfig_exit(void)
762 kobject_put(hc_kobj);