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