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