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_u32(const u8 *pld, u16 pld_len, char *buf)
316 u32 data; // cpu-endian
318 /* Caller ensures pld_len > 0 */
319 if (pld_len % sizeof(data))
325 out += sprintf(out, "0x%08x\n", data);
327 } while ((pld_len -= sizeof(data)));
333 * The MAC is stored network-endian on all devices, in 2 32-bit segments:
334 * <XX:XX:XX:XX> <XX:XX:00:00>. Kernel print has us covered.
336 static ssize_t hc_tag_show_mac(const u8 *pld, u16 pld_len, char *buf)
341 return sprintf(buf, "%pM\n", pld);
345 * Print HW options in a human readable way:
346 * The raw number and in decoded form
348 static ssize_t hc_tag_show_hwoptions(const u8 *pld, u16 pld_len, char *buf)
351 u32 data; // cpu-endian
354 if (sizeof(data) != pld_len)
358 out += sprintf(out, "raw\t\t: 0x%08x\n\n", data);
360 for (i = 0; i < ARRAY_SIZE(hc_hwopts); i++)
361 out += sprintf(out, "%s: %s\n", hc_hwopts[i].str,
362 (data & hc_hwopts[i].bit) ? "true" : "false");
367 static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj,
368 struct bin_attribute *attr, char *buf,
369 loff_t off, size_t count);
371 static struct hc_wlan_attr {
372 struct bin_attribute battr;
375 } hc_wlandata_battr = {
376 .battr = __BIN_ATTR(wlan_data, S_IRUSR, hc_wlan_data_bin_read, NULL, 0),
379 static ssize_t hc_attr_show(struct kobject *kobj, struct kobj_attribute *attr,
382 /* Array of known tags to publish in sysfs */
383 static struct hc_attr {
385 ssize_t (* const tshow)(const u8 *pld, u16 pld_len, char *buf);
386 struct kobj_attribute kattr;
391 .tag_id = RB_ID_FLASH_INFO,
392 .tshow = hc_tag_show_u32,
393 .kattr = __ATTR(flash_info, S_IRUSR, hc_attr_show, NULL),
395 .tag_id = RB_ID_MAC_ADDRESS_PACK,
396 .tshow = hc_tag_show_mac,
397 .kattr = __ATTR(mac_base, S_IRUSR, hc_attr_show, NULL),
399 .tag_id = RB_ID_BOARD_PRODUCT_CODE,
400 .tshow = routerboot_tag_show_string,
401 .kattr = __ATTR(board_product_code, S_IRUSR, hc_attr_show, NULL),
403 .tag_id = RB_ID_BIOS_VERSION,
404 .tshow = routerboot_tag_show_string,
405 .kattr = __ATTR(booter_version, S_IRUSR, hc_attr_show, NULL),
407 .tag_id = RB_ID_SERIAL_NUMBER,
408 .tshow = routerboot_tag_show_string,
409 .kattr = __ATTR(board_serial, S_IRUSR, hc_attr_show, NULL),
411 .tag_id = RB_ID_MEMORY_SIZE,
412 .tshow = hc_tag_show_u32,
413 .kattr = __ATTR(mem_size, S_IRUSR, hc_attr_show, NULL),
415 .tag_id = RB_ID_MAC_ADDRESS_COUNT,
416 .tshow = hc_tag_show_u32,
417 .kattr = __ATTR(mac_count, S_IRUSR, hc_attr_show, NULL),
419 .tag_id = RB_ID_HW_OPTIONS,
420 .tshow = hc_tag_show_hwoptions,
421 .kattr = __ATTR(hw_options, S_IRUSR, hc_attr_show, NULL),
423 .tag_id = RB_ID_WLAN_DATA,
426 .tag_id = RB_ID_BOARD_IDENTIFIER,
427 .tshow = routerboot_tag_show_string,
428 .kattr = __ATTR(board_identifier, S_IRUSR, hc_attr_show, NULL),
430 .tag_id = RB_ID_PRODUCT_NAME,
431 .tshow = routerboot_tag_show_string,
432 .kattr = __ATTR(product_name, S_IRUSR, hc_attr_show, NULL),
434 .tag_id = RB_ID_DEFCONF,
435 .tshow = routerboot_tag_show_string,
436 .kattr = __ATTR(defconf, S_IRUSR, hc_attr_show, NULL),
441 * If the RB_ID_WLAN_DATA payload starts with RB_MAGIC_ERD, then past
442 * that magic number the payload itself contains a routerboot tag node
443 * locating the LZO-compressed calibration data at id 0x1.
445 static int hc_wlan_data_unpack_erd(const u8 *inbuf, size_t inlen,
446 void *outbuf, size_t *outlen)
448 u16 lzo_ofs, lzo_len;
451 /* Find embedded tag */
452 ret = routerboot_tag_find(inbuf, inlen, 0x1, // always id 1
455 pr_debug(RB_HC_PR_PFX "ERD data not found\n");
459 if (lzo_len > inlen) {
460 pr_debug(RB_HC_PR_PFX "Invalid ERD data length\n");
465 ret = lzo1x_decompress_safe(inbuf+lzo_ofs, lzo_len, outbuf, outlen);
467 pr_debug(RB_HC_PR_PFX "LZO decompression error (%d)\n", ret);
474 * If the RB_ID_WLAN_DATA payload starts with RB_MAGIC_LZOR, then past
475 * that magic number is a payload that must be appended to the hc_lzor_prefix,
476 * the resulting blob is LZO-compressed. In the LZO decompression result,
477 * the RB_MAGIC_ERD magic number (aligned) must be located. Following that
478 * magic, there is a routerboot tag node (id 0x1) locating the RLE-encoded
479 * calibration data payload.
481 static int hc_wlan_data_unpack_lzor(const u8 *inbuf, size_t inlen,
482 void *outbuf, size_t *outlen)
484 u16 rle_ofs, rle_len;
487 size_t templen, lzo_len;
490 lzo_len = inlen + sizeof(hc_lzor_prefix);
491 if (lzo_len > *outlen)
494 /* Temporary buffer same size as the outbuf */
496 tempbuf = kmalloc(templen, GFP_KERNEL);
500 /* Concatenate into the outbuf */
501 memcpy(outbuf, hc_lzor_prefix, sizeof(hc_lzor_prefix));
502 memcpy(outbuf + sizeof(hc_lzor_prefix), inbuf, inlen);
504 /* LZO-decompress lzo_len bytes of outbuf into the tempbuf */
505 ret = lzo1x_decompress_safe(outbuf, lzo_len, tempbuf, &templen);
507 if (LZO_E_INPUT_NOT_CONSUMED == ret) {
509 * The tag length appears to always be aligned (probably
510 * because it is the "root" RB_ID_WLAN_DATA tag), thus
511 * the LZO payload may be padded, which can trigger a
512 * spurious error which we ignore here.
514 pr_debug(RB_HC_PR_PFX "LZOR: LZO EOF before buffer end - this may be harmless\n");
516 pr_debug(RB_HC_PR_PFX "LZOR: LZO decompression error (%d)\n", ret);
522 * Post decompression we have a blob (possibly byproduct of the lzo
523 * dictionary). We need to find RB_MAGIC_ERD. The magic number seems to
524 * be 32bit-aligned in the decompression output.
526 needle = (const u32 *)tempbuf;
527 while (RB_MAGIC_ERD != *needle++) {
528 if ((u8 *)needle >= tempbuf+templen) {
529 pr_debug(RB_HC_PR_PFX "LZOR: ERD magic not found\n");
534 templen -= (u8 *)needle - tempbuf;
536 /* Past magic. Look for tag node */
537 ret = routerboot_tag_find((u8 *)needle, templen, 0x1, &rle_ofs, &rle_len);
539 pr_debug(RB_HC_PR_PFX "LZOR: RLE data not found\n");
543 if (rle_len > templen) {
544 pr_debug(RB_HC_PR_PFX "LZOR: Invalid RLE data length\n");
549 /* RLE-decode tempbuf from needle back into the outbuf */
550 ret = routerboot_rle_decode((u8 *)needle+rle_ofs, rle_len, outbuf, outlen);
552 pr_debug(RB_HC_PR_PFX "LZOR: RLE decoding error (%d)\n", ret);
559 static int hc_wlan_data_unpack(const size_t tofs, size_t tlen,
560 void *outbuf, size_t *outlen)
566 /* Caller ensure tlen > 0. tofs is aligned */
567 if ((tofs + tlen) > hc_buflen)
570 lbuf = hc_buf + tofs;
571 magic = *(u32 *)lbuf;
577 lbuf += sizeof(magic);
578 tlen -= sizeof(magic);
579 ret = hc_wlan_data_unpack_lzor(lbuf, tlen, outbuf, outlen);
583 lbuf += sizeof(magic);
584 tlen -= sizeof(magic);
585 ret = hc_wlan_data_unpack_erd(lbuf, tlen, outbuf, outlen);
589 * If the RB_ID_WLAN_DATA payload doesn't start with a
590 * magic number, the payload itself is the raw RLE-encoded
593 ret = routerboot_rle_decode(lbuf, tlen, outbuf, outlen);
595 pr_debug(RB_HC_PR_PFX "RLE decoding error (%d)\n", ret);
602 static ssize_t hc_attr_show(struct kobject *kobj, struct kobj_attribute *attr,
605 const struct hc_attr *hc_attr;
609 hc_attr = container_of(attr, typeof(*hc_attr), kattr);
611 if (!hc_attr->pld_len)
614 pld = hc_buf + hc_attr->pld_ofs;
615 pld_len = hc_attr->pld_len;
617 return hc_attr->tshow(pld, pld_len, buf);
621 * This function will allocate and free memory every time it is called. This
622 * is not the fastest way to do this, but since the data is rarely read (mainly
623 * at boot time to load wlan caldata), this makes it possible to save memory for
626 static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj,
627 struct bin_attribute *attr, char *buf,
628 loff_t off, size_t count)
630 struct hc_wlan_attr *hc_wattr;
635 hc_wattr = container_of(attr, typeof(*hc_wattr), battr);
637 if (!hc_wattr->pld_len)
640 outlen = RB_ART_SIZE;
642 /* Don't bother unpacking if the source is already too large */
643 if (hc_wattr->pld_len > outlen)
646 outbuf = kmalloc(outlen, GFP_KERNEL);
650 ret = hc_wlan_data_unpack(hc_wattr->pld_ofs, hc_wattr->pld_len, outbuf, &outlen);
661 if (off + count > outlen)
662 count = outlen - off;
664 memcpy(buf, outbuf + off, count);
670 int __init rb_hardconfig_init(struct kobject *rb_kobj)
672 struct mtd_info *mtd;
673 size_t bytes_read, buflen;
681 // TODO allow override
682 mtd = get_mtd_device_nm(RB_MTD_HARD_CONFIG);
686 hc_buflen = mtd->size;
687 hc_buf = kmalloc(hc_buflen, GFP_KERNEL);
691 ret = mtd_read(mtd, 0, hc_buflen, &bytes_read, hc_buf);
696 if (bytes_read != hc_buflen) {
701 /* Check we have what we expect */
702 magic = *(const u32 *)hc_buf;
703 if (RB_MAGIC_HARD != magic) {
709 buf = hc_buf + sizeof(magic);
710 buflen = hc_buflen - sizeof(magic);
714 hc_kobj = kobject_create_and_add(RB_MTD_HARD_CONFIG, rb_kobj);
718 /* Locate and publish all known tags */
719 for (i = 0; i < ARRAY_SIZE(hc_attrs); i++) {
720 ret = routerboot_tag_find(buf, buflen, hc_attrs[i].tag_id,
721 &hc_attrs[i].pld_ofs, &hc_attrs[i].pld_len);
723 hc_attrs[i].pld_ofs = hc_attrs[i].pld_len = 0;
727 /* Account for skipped magic */
728 hc_attrs[i].pld_ofs += sizeof(magic);
730 /* Special case RB_ID_WLAN_DATA to prep and create the binary attribute */
731 if ((RB_ID_WLAN_DATA == hc_attrs[i].tag_id) && hc_attrs[i].pld_len) {
732 hc_wlandata_battr.pld_ofs = hc_attrs[i].pld_ofs;
733 hc_wlandata_battr.pld_len = hc_attrs[i].pld_len;
735 ret = sysfs_create_bin_file(hc_kobj, &hc_wlandata_battr.battr);
737 pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n",
738 hc_wlandata_battr.battr.attr.name, ret);
740 /* All other tags are published via standard attributes */
742 ret = sysfs_create_file(hc_kobj, &hc_attrs[i].kattr.attr);
744 pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n",
745 hc_attrs[i].kattr.attr.name, ret);
749 pr_info("MikroTik RouterBOARD hardware configuration sysfs driver v" RB_HARDCONFIG_VER "\n");
759 void __exit rb_hardconfig_exit(void)
761 kobject_put(hc_kobj);