4bcbb75e6f0218ee60e67ecfe2457606fc1831d5
[openwrt/staging/luka.git] / target / linux / generic / files / drivers / platform / mikrotik / rb_hardconfig.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Driver for MikroTik RouterBoot hard config.
4 *
5 * Copyright (C) 2020 Thibaut VARĂˆNE <hacks+kernel@slashdirt.org>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published
9 * by the Free Software Foundation.
10 *
11 * This driver exposes the data encoded in the "hard_config" flash segment of
12 * MikroTik RouterBOARDs devices. It presents the data in a sysfs folder
13 * named "hard_config". The WLAN calibration data is available on demand via
14 * the 'wlan_data' sysfs file in that folder.
15 *
16 * This driver permanently allocates a chunk of RAM as large as the hard_config
17 * MTD partition, although it is technically possible to operate entirely from
18 * the MTD device without using a local buffer (except when requesting WLAN
19 * calibration data), at the cost of a performance penalty.
20 *
21 * Note: PAGE_SIZE is assumed to be >= 4K, hence the device attribute show
22 * routines need not check for output overflow.
23 *
24 * Some constant defines extracted from routerboot.{c,h} by Gabor Juhos
25 * <juhosg@openwrt.org>
26 */
27
28 #include <linux/types.h>
29 #include <linux/init.h>
30 #include <linux/kernel.h>
31 #include <linux/slab.h>
32 #include <linux/errno.h>
33 #include <linux/kobject.h>
34 #include <linux/bitops.h>
35 #include <linux/string.h>
36 #include <linux/mtd/mtd.h>
37 #include <linux/sysfs.h>
38 #include <linux/lzo.h>
39
40 #include "routerboot.h"
41
42 #define RB_HARDCONFIG_VER "0.04"
43 #define RB_HC_PR_PFX "[rb_hardconfig] "
44
45 /* ID values for hardware settings */
46 #define RB_ID_FLASH_INFO 0x03
47 #define RB_ID_MAC_ADDRESS_PACK 0x04
48 #define RB_ID_BOARD_PRODUCT_CODE 0x05
49 #define RB_ID_BIOS_VERSION 0x06
50 #define RB_ID_SDRAM_TIMINGS 0x08
51 #define RB_ID_DEVICE_TIMINGS 0x09
52 #define RB_ID_SOFTWARE_ID 0x0A
53 #define RB_ID_SERIAL_NUMBER 0x0B
54 #define RB_ID_MEMORY_SIZE 0x0D
55 #define RB_ID_MAC_ADDRESS_COUNT 0x0E
56 #define RB_ID_HW_OPTIONS 0x15
57 #define RB_ID_WLAN_DATA 0x16
58 #define RB_ID_BOARD_IDENTIFIER 0x17
59 #define RB_ID_PRODUCT_NAME 0x21
60 #define RB_ID_DEFCONF 0x26
61
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)
77
78 static struct kobject *hc_kobj;
79 static u8 *hc_buf; // ro buffer after init(): no locking required
80 static size_t hc_buflen;
81
82 /*
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
89 */
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
264 };
265
266 /* Array of known hw_options bits with human-friendly parsing */
267 static struct hc_hwopt {
268 const u32 bit;
269 const char *str;
270 } const hc_hwopts[] = {
271 {
272 .bit = RB_HW_OPT_NO_UART,
273 .str = "no UART\t\t",
274 }, {
275 .bit = RB_HW_OPT_HAS_VOLTAGE,
276 .str = "has Vreg\t",
277 }, {
278 .bit = RB_HW_OPT_HAS_USB,
279 .str = "has usb\t\t",
280 }, {
281 .bit = RB_HW_OPT_HAS_ATTINY,
282 .str = "has ATtiny\t",
283 }, {
284 .bit = RB_HW_OPT_NO_NAND,
285 .str = "no NAND\t\t",
286 }, {
287 .bit = RB_HW_OPT_HAS_LCD,
288 .str = "has LCD\t\t",
289 }, {
290 .bit = RB_HW_OPT_HAS_POE_OUT,
291 .str = "has POE out\t",
292 }, {
293 .bit = RB_HW_OPT_HAS_uSD,
294 .str = "has MicroSD\t",
295 }, {
296 .bit = RB_HW_OPT_HAS_SIM,
297 .str = "has SIM\t\t",
298 }, {
299 .bit = RB_HW_OPT_HAS_SFP,
300 .str = "has SFP\t\t",
301 }, {
302 .bit = RB_HW_OPT_HAS_WIFI,
303 .str = "has WiFi\t",
304 }, {
305 .bit = RB_HW_OPT_HAS_TS_FOR_ADC,
306 .str = "has TS ADC\t",
307 }, {
308 .bit = RB_HW_OPT_HAS_PLC,
309 .str = "has PLC\t\t",
310 },
311 };
312
313 static ssize_t hc_tag_show_string(const u8 *pld, u16 pld_len, char *buf)
314 {
315 return snprintf(buf, pld_len+1, "%s\n", pld);
316 }
317
318 static ssize_t hc_tag_show_u32(const u8 *pld, u16 pld_len, char *buf)
319 {
320 char *out = buf;
321 u32 data; // cpu-endian
322
323 /* Caller ensures pld_len > 0 */
324 if (pld_len % sizeof(data))
325 return -EINVAL;
326
327 data = *(u32 *)pld;
328
329 do {
330 out += sprintf(out, "0x%08x\n", data);
331 data++;
332 } while ((pld_len -= sizeof(data)));
333
334 return out - buf;
335 }
336
337 /*
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.
340 */
341 static ssize_t hc_tag_show_mac(const u8 *pld, u16 pld_len, char *buf)
342 {
343 if (8 != pld_len)
344 return -EINVAL;
345
346 return sprintf(buf, "%pM\n", pld);
347 }
348
349 /*
350 * Print HW options in a human readable way:
351 * The raw number and in decoded form
352 */
353 static ssize_t hc_tag_show_hwoptions(const u8 *pld, u16 pld_len, char *buf)
354 {
355 char *out = buf;
356 u32 data; // cpu-endian
357 int i;
358
359 if (sizeof(data) != pld_len)
360 return -EINVAL;
361
362 data = *(u32 *)pld;
363 out += sprintf(out, "raw\t\t: 0x%08x\n\n", data);
364
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");
368
369 return out - buf;
370 }
371
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);
375
376 static struct hc_wlan_attr {
377 struct bin_attribute battr;
378 u16 pld_ofs;
379 u16 pld_len;
380 } hc_wlandata_battr = {
381 .battr = __BIN_ATTR(wlan_data, S_IRUSR, hc_wlan_data_bin_read, NULL, 0),
382 };
383
384 static ssize_t hc_attr_show(struct kobject *kobj, struct kobj_attribute *attr,
385 char *buf);
386
387 /* Array of known tags to publish in sysfs */
388 static struct hc_attr {
389 const u16 tag_id;
390 ssize_t (* const tshow)(const u8 *pld, u16 pld_len, char *buf);
391 struct kobj_attribute kattr;
392 u16 pld_ofs;
393 u16 pld_len;
394 } hc_attrs[] = {
395 {
396 .tag_id = RB_ID_FLASH_INFO,
397 .tshow = hc_tag_show_u32,
398 .kattr = __ATTR(flash_info, S_IRUSR, hc_attr_show, NULL),
399 }, {
400 .tag_id = RB_ID_MAC_ADDRESS_PACK,
401 .tshow = hc_tag_show_mac,
402 .kattr = __ATTR(mac_base, S_IRUSR, hc_attr_show, NULL),
403 }, {
404 .tag_id = RB_ID_BOARD_PRODUCT_CODE,
405 .tshow = hc_tag_show_string,
406 .kattr = __ATTR(board_product_code, S_IRUSR, hc_attr_show, NULL),
407 }, {
408 .tag_id = RB_ID_BIOS_VERSION,
409 .tshow = hc_tag_show_string,
410 .kattr = __ATTR(booter_version, S_IRUSR, hc_attr_show, NULL),
411 }, {
412 .tag_id = RB_ID_SERIAL_NUMBER,
413 .tshow = hc_tag_show_string,
414 .kattr = __ATTR(board_serial, S_IRUSR, hc_attr_show, NULL),
415 }, {
416 .tag_id = RB_ID_MEMORY_SIZE,
417 .tshow = hc_tag_show_u32,
418 .kattr = __ATTR(mem_size, S_IRUSR, hc_attr_show, NULL),
419 }, {
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),
423 }, {
424 .tag_id = RB_ID_HW_OPTIONS,
425 .tshow = hc_tag_show_hwoptions,
426 .kattr = __ATTR(hw_options, S_IRUSR, hc_attr_show, NULL),
427 }, {
428 .tag_id = RB_ID_WLAN_DATA,
429 .tshow = NULL,
430 }, {
431 .tag_id = RB_ID_BOARD_IDENTIFIER,
432 .tshow = hc_tag_show_string,
433 .kattr = __ATTR(board_identifier, S_IRUSR, hc_attr_show, NULL),
434 }, {
435 .tag_id = RB_ID_PRODUCT_NAME,
436 .tshow = hc_tag_show_string,
437 .kattr = __ATTR(product_name, S_IRUSR, hc_attr_show, NULL),
438 }, {
439 .tag_id = RB_ID_DEFCONF,
440 .tshow = hc_tag_show_string,
441 .kattr = __ATTR(defconf, S_IRUSR, hc_attr_show, NULL),
442 }
443 };
444
445 /*
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.
449 */
450 static int hc_wlan_data_unpack_erd(const u8 *inbuf, size_t inlen,
451 void *outbuf, size_t *outlen)
452 {
453 u16 lzo_ofs, lzo_len;
454 int ret;
455
456 /* Find embedded tag */
457 ret = routerboot_tag_find(inbuf, inlen, 0x1, // always id 1
458 &lzo_ofs, &lzo_len);
459 if (ret) {
460 pr_debug(RB_HC_PR_PFX "ERD data not found\n");
461 goto fail;
462 }
463
464 if (lzo_len > inlen) {
465 pr_debug(RB_HC_PR_PFX "Invalid ERD data length\n");
466 ret = -EINVAL;
467 goto fail;
468 }
469
470 ret = lzo1x_decompress_safe(inbuf+lzo_ofs, lzo_len, outbuf, outlen);
471 if (ret)
472 pr_debug(RB_HC_PR_PFX "LZO decompression error (%d)\n", ret);
473
474 fail:
475 return ret;
476 }
477
478 /*
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.
485 */
486 static int hc_wlan_data_unpack_lzor(const u8 *inbuf, size_t inlen,
487 void *outbuf, size_t *outlen)
488 {
489 u16 rle_ofs, rle_len;
490 const u32 *needle;
491 u8 *tempbuf;
492 size_t templen, lzo_len;
493 int ret;
494
495 lzo_len = inlen + sizeof(hc_lzor_prefix);
496 if (lzo_len > *outlen)
497 return -EFBIG;
498
499 /* Temporary buffer same size as the outbuf */
500 templen = *outlen;
501 tempbuf = kmalloc(templen, GFP_KERNEL);
502 if (!outbuf)
503 return -ENOMEM;
504
505 /* Concatenate into the outbuf */
506 memcpy(outbuf, hc_lzor_prefix, sizeof(hc_lzor_prefix));
507 memcpy(outbuf + sizeof(hc_lzor_prefix), inbuf, inlen);
508
509 /* LZO-decompress lzo_len bytes of outbuf into the tempbuf */
510 ret = lzo1x_decompress_safe(outbuf, lzo_len, tempbuf, &templen);
511 if (ret) {
512 if (LZO_E_INPUT_NOT_CONSUMED == ret) {
513 /*
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.
518 */
519 pr_debug(RB_HC_PR_PFX "LZOR: LZO EOF before buffer end - this may be harmless\n");
520 } else {
521 pr_debug(RB_HC_PR_PFX "LZOR: LZO decompression error (%d)\n", ret);
522 goto fail;
523 }
524 }
525
526 /*
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.
530 */
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");
535 ret = -ENODATA;
536 goto fail;
537 }
538 };
539 templen -= (u8 *)needle - tempbuf;
540
541 /* Past magic. Look for tag node */
542 ret = routerboot_tag_find((u8 *)needle, templen, 0x1, &rle_ofs, &rle_len);
543 if (ret) {
544 pr_debug(RB_HC_PR_PFX "LZOR: RLE data not found\n");
545 goto fail;
546 }
547
548 if (rle_len > templen) {
549 pr_debug(RB_HC_PR_PFX "LZOR: Invalid RLE data length\n");
550 ret = -EINVAL;
551 goto fail;
552 }
553
554 /* RLE-decode tempbuf from needle back into the outbuf */
555 ret = routerboot_rle_decode((u8 *)needle+rle_ofs, rle_len, outbuf, outlen);
556 if (ret)
557 pr_debug(RB_HC_PR_PFX "LZOR: RLE decoding error (%d)\n", ret);
558
559 fail:
560 kfree(tempbuf);
561 return ret;
562 }
563
564 static int hc_wlan_data_unpack(const size_t tofs, size_t tlen,
565 void *outbuf, size_t *outlen)
566 {
567 const u8 *lbuf;
568 u32 magic;
569 int ret;
570
571 /* Caller ensure tlen > 0. tofs is aligned */
572 if ((tofs + tlen) > hc_buflen)
573 return -EIO;
574
575 lbuf = hc_buf + tofs;
576 magic = *(u32 *)lbuf;
577
578 ret = -ENODATA;
579 switch (magic) {
580 case RB_MAGIC_LZOR:
581 /* Skip magic */
582 lbuf += sizeof(magic);
583 tlen -= sizeof(magic);
584 ret = hc_wlan_data_unpack_lzor(lbuf, tlen, outbuf, outlen);
585 break;
586 case RB_MAGIC_ERD:
587 /* Skip magic */
588 lbuf += sizeof(magic);
589 tlen -= sizeof(magic);
590 ret = hc_wlan_data_unpack_erd(lbuf, tlen, outbuf, outlen);
591 break;
592 default:
593 /*
594 * If the RB_ID_WLAN_DATA payload doesn't start with a
595 * magic number, the payload itself is the raw RLE-encoded
596 * calibration data.
597 */
598 ret = routerboot_rle_decode(lbuf, tlen, outbuf, outlen);
599 if (ret)
600 pr_debug(RB_HC_PR_PFX "RLE decoding error (%d)\n", ret);
601 break;
602 }
603
604 return ret;
605 }
606
607 static ssize_t hc_attr_show(struct kobject *kobj, struct kobj_attribute *attr,
608 char *buf)
609 {
610 const struct hc_attr *hc_attr;
611 const u8 *pld;
612 u16 pld_len;
613
614 hc_attr = container_of(attr, typeof(*hc_attr), kattr);
615
616 if (!hc_attr->pld_len)
617 return -ENOENT;
618
619 pld = hc_buf + hc_attr->pld_ofs;
620 pld_len = hc_attr->pld_len;
621
622 return hc_attr->tshow(pld, pld_len, buf);
623 }
624
625 /*
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
629 * the system.
630 */
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)
634 {
635 struct hc_wlan_attr *hc_wattr;
636 size_t outlen;
637 void *outbuf;
638 int ret;
639
640 hc_wattr = container_of(attr, typeof(*hc_wattr), battr);
641
642 if (!hc_wattr->pld_len)
643 return -ENOENT;
644
645 outlen = RB_ART_SIZE;
646
647 /* Don't bother unpacking if the source is already too large */
648 if (hc_wattr->pld_len > outlen)
649 return -EFBIG;
650
651 outbuf = kmalloc(outlen, GFP_KERNEL);
652 if (!outbuf)
653 return -ENOMEM;
654
655 ret = hc_wlan_data_unpack(hc_wattr->pld_ofs, hc_wattr->pld_len, outbuf, &outlen);
656 if (ret) {
657 kfree(outbuf);
658 return ret;
659 }
660
661 if (off >= outlen) {
662 kfree(outbuf);
663 return 0;
664 }
665
666 if (off + count > outlen)
667 count = outlen - off;
668
669 memcpy(buf, outbuf + off, count);
670
671 kfree(outbuf);
672 return count;
673 }
674
675 int __init rb_hardconfig_init(struct kobject *rb_kobj)
676 {
677 struct mtd_info *mtd;
678 size_t bytes_read, buflen;
679 const u8 *buf;
680 int i, ret;
681 u32 magic;
682
683 // TODO allow override
684 mtd = get_mtd_device_nm(RB_MTD_HARD_CONFIG);
685 if (IS_ERR(mtd))
686 return -ENODEV;
687
688 hc_buflen = mtd->size;
689 hc_buf = kmalloc(hc_buflen, GFP_KERNEL);
690 if (!hc_buf)
691 return -ENOMEM;
692
693 ret = mtd_read(mtd, 0, hc_buflen, &bytes_read, hc_buf);
694
695 if (ret)
696 goto fail;
697
698 if (bytes_read != hc_buflen) {
699 ret = -EIO;
700 goto fail;
701 }
702
703 /* Check we have what we expect */
704 magic = *(const u32 *)hc_buf;
705 if (RB_MAGIC_HARD != magic) {
706 ret = -EINVAL;
707 goto fail;
708 }
709
710 /* Skip magic */
711 buf = hc_buf + sizeof(magic);
712 buflen = hc_buflen - sizeof(magic);
713
714 /* Populate sysfs */
715 ret = -ENOMEM;
716 hc_kobj = kobject_create_and_add(RB_MTD_HARD_CONFIG, rb_kobj);
717 if (!hc_kobj)
718 goto fail;
719
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);
724 if (ret) {
725 hc_attrs[i].pld_ofs = hc_attrs[i].pld_len = 0;
726 continue;
727 }
728
729 /* Account for skipped magic */
730 hc_attrs[i].pld_ofs += sizeof(magic);
731
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;
736
737 ret = sysfs_create_bin_file(hc_kobj, &hc_wlandata_battr.battr);
738 if (ret)
739 pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n",
740 hc_wlandata_battr.battr.attr.name, ret);
741 }
742 /* All other tags are published via standard attributes */
743 else {
744 ret = sysfs_create_file(hc_kobj, &hc_attrs[i].kattr.attr);
745 if (ret)
746 pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n",
747 hc_attrs[i].kattr.attr.name, ret);
748 }
749 }
750
751 pr_info("MikroTik RouterBOARD hardware configuration sysfs driver v" RB_HARDCONFIG_VER "\n");
752
753 return 0;
754
755 fail:
756 kfree(hc_buf);
757 return ret;
758 }
759
760 void __exit rb_hardconfig_exit(void)
761 {
762 kobject_put(hc_kobj);
763 kfree(hc_buf);
764 }