a03aa21b4d55ff7a54f9693c99cad2b0d95bd1d7
[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 * 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_u32(const u8 *pld, u16 pld_len, char *buf)
314 {
315 char *out = buf;
316 u32 data; // cpu-endian
317
318 /* Caller ensures pld_len > 0 */
319 if (pld_len % sizeof(data))
320 return -EINVAL;
321
322 data = *(u32 *)pld;
323
324 do {
325 out += sprintf(out, "0x%08x\n", data);
326 data++;
327 } while ((pld_len -= sizeof(data)));
328
329 return out - buf;
330 }
331
332 /*
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.
335 */
336 static ssize_t hc_tag_show_mac(const u8 *pld, u16 pld_len, char *buf)
337 {
338 if (8 != pld_len)
339 return -EINVAL;
340
341 return sprintf(buf, "%pM\n", pld);
342 }
343
344 /*
345 * Print HW options in a human readable way:
346 * The raw number and in decoded form
347 */
348 static ssize_t hc_tag_show_hwoptions(const u8 *pld, u16 pld_len, char *buf)
349 {
350 char *out = buf;
351 u32 data; // cpu-endian
352 int i;
353
354 if (sizeof(data) != pld_len)
355 return -EINVAL;
356
357 data = *(u32 *)pld;
358 out += sprintf(out, "raw\t\t: 0x%08x\n\n", data);
359
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");
363
364 return out - buf;
365 }
366
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);
370
371 static struct hc_wlan_attr {
372 struct bin_attribute battr;
373 u16 pld_ofs;
374 u16 pld_len;
375 } hc_wlandata_battr = {
376 .battr = __BIN_ATTR(wlan_data, S_IRUSR, hc_wlan_data_bin_read, NULL, 0),
377 };
378
379 static ssize_t hc_attr_show(struct kobject *kobj, struct kobj_attribute *attr,
380 char *buf);
381
382 /* Array of known tags to publish in sysfs */
383 static struct hc_attr {
384 const u16 tag_id;
385 ssize_t (* const tshow)(const u8 *pld, u16 pld_len, char *buf);
386 struct kobj_attribute kattr;
387 u16 pld_ofs;
388 u16 pld_len;
389 } hc_attrs[] = {
390 {
391 .tag_id = RB_ID_FLASH_INFO,
392 .tshow = hc_tag_show_u32,
393 .kattr = __ATTR(flash_info, S_IRUSR, hc_attr_show, NULL),
394 }, {
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),
398 }, {
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),
402 }, {
403 .tag_id = RB_ID_BIOS_VERSION,
404 .tshow = routerboot_tag_show_string,
405 .kattr = __ATTR(booter_version, S_IRUSR, hc_attr_show, NULL),
406 }, {
407 .tag_id = RB_ID_SERIAL_NUMBER,
408 .tshow = routerboot_tag_show_string,
409 .kattr = __ATTR(board_serial, S_IRUSR, hc_attr_show, NULL),
410 }, {
411 .tag_id = RB_ID_MEMORY_SIZE,
412 .tshow = hc_tag_show_u32,
413 .kattr = __ATTR(mem_size, S_IRUSR, hc_attr_show, NULL),
414 }, {
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),
418 }, {
419 .tag_id = RB_ID_HW_OPTIONS,
420 .tshow = hc_tag_show_hwoptions,
421 .kattr = __ATTR(hw_options, S_IRUSR, hc_attr_show, NULL),
422 }, {
423 .tag_id = RB_ID_WLAN_DATA,
424 .tshow = NULL,
425 }, {
426 .tag_id = RB_ID_BOARD_IDENTIFIER,
427 .tshow = routerboot_tag_show_string,
428 .kattr = __ATTR(board_identifier, S_IRUSR, hc_attr_show, NULL),
429 }, {
430 .tag_id = RB_ID_PRODUCT_NAME,
431 .tshow = routerboot_tag_show_string,
432 .kattr = __ATTR(product_name, S_IRUSR, hc_attr_show, NULL),
433 }, {
434 .tag_id = RB_ID_DEFCONF,
435 .tshow = routerboot_tag_show_string,
436 .kattr = __ATTR(defconf, S_IRUSR, hc_attr_show, NULL),
437 }
438 };
439
440 /*
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.
444 */
445 static int hc_wlan_data_unpack_erd(const u8 *inbuf, size_t inlen,
446 void *outbuf, size_t *outlen)
447 {
448 u16 lzo_ofs, lzo_len;
449 int ret;
450
451 /* Find embedded tag */
452 ret = routerboot_tag_find(inbuf, inlen, 0x1, // always id 1
453 &lzo_ofs, &lzo_len);
454 if (ret) {
455 pr_debug(RB_HC_PR_PFX "ERD data not found\n");
456 goto fail;
457 }
458
459 if (lzo_len > inlen) {
460 pr_debug(RB_HC_PR_PFX "Invalid ERD data length\n");
461 ret = -EINVAL;
462 goto fail;
463 }
464
465 ret = lzo1x_decompress_safe(inbuf+lzo_ofs, lzo_len, outbuf, outlen);
466 if (ret)
467 pr_debug(RB_HC_PR_PFX "LZO decompression error (%d)\n", ret);
468
469 fail:
470 return ret;
471 }
472
473 /*
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.
480 */
481 static int hc_wlan_data_unpack_lzor(const u8 *inbuf, size_t inlen,
482 void *outbuf, size_t *outlen)
483 {
484 u16 rle_ofs, rle_len;
485 const u32 *needle;
486 u8 *tempbuf;
487 size_t templen, lzo_len;
488 int ret;
489
490 lzo_len = inlen + sizeof(hc_lzor_prefix);
491 if (lzo_len > *outlen)
492 return -EFBIG;
493
494 /* Temporary buffer same size as the outbuf */
495 templen = *outlen;
496 tempbuf = kmalloc(templen, GFP_KERNEL);
497 if (!outbuf)
498 return -ENOMEM;
499
500 /* Concatenate into the outbuf */
501 memcpy(outbuf, hc_lzor_prefix, sizeof(hc_lzor_prefix));
502 memcpy(outbuf + sizeof(hc_lzor_prefix), inbuf, inlen);
503
504 /* LZO-decompress lzo_len bytes of outbuf into the tempbuf */
505 ret = lzo1x_decompress_safe(outbuf, lzo_len, tempbuf, &templen);
506 if (ret) {
507 if (LZO_E_INPUT_NOT_CONSUMED == ret) {
508 /*
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.
513 */
514 pr_debug(RB_HC_PR_PFX "LZOR: LZO EOF before buffer end - this may be harmless\n");
515 } else {
516 pr_debug(RB_HC_PR_PFX "LZOR: LZO decompression error (%d)\n", ret);
517 goto fail;
518 }
519 }
520
521 /*
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.
525 */
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");
530 ret = -ENODATA;
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 const 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 hc_buf = NULL;
679 hc_kobj = NULL;
680
681 // TODO allow override
682 mtd = get_mtd_device_nm(RB_MTD_HARD_CONFIG);
683 if (IS_ERR(mtd))
684 return -ENODEV;
685
686 hc_buflen = mtd->size;
687 hc_buf = kmalloc(hc_buflen, GFP_KERNEL);
688 if (!hc_buf)
689 return -ENOMEM;
690
691 ret = mtd_read(mtd, 0, hc_buflen, &bytes_read, hc_buf);
692
693 if (ret)
694 goto fail;
695
696 if (bytes_read != hc_buflen) {
697 ret = -EIO;
698 goto fail;
699 }
700
701 /* Check we have what we expect */
702 magic = *(const u32 *)hc_buf;
703 if (RB_MAGIC_HARD != magic) {
704 ret = -EINVAL;
705 goto fail;
706 }
707
708 /* Skip magic */
709 buf = hc_buf + sizeof(magic);
710 buflen = hc_buflen - sizeof(magic);
711
712 /* Populate sysfs */
713 ret = -ENOMEM;
714 hc_kobj = kobject_create_and_add(RB_MTD_HARD_CONFIG, rb_kobj);
715 if (!hc_kobj)
716 goto fail;
717
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);
722 if (ret) {
723 hc_attrs[i].pld_ofs = hc_attrs[i].pld_len = 0;
724 continue;
725 }
726
727 /* Account for skipped magic */
728 hc_attrs[i].pld_ofs += sizeof(magic);
729
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;
734
735 ret = sysfs_create_bin_file(hc_kobj, &hc_wlandata_battr.battr);
736 if (ret)
737 pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n",
738 hc_wlandata_battr.battr.attr.name, ret);
739 }
740 /* All other tags are published via standard attributes */
741 else {
742 ret = sysfs_create_file(hc_kobj, &hc_attrs[i].kattr.attr);
743 if (ret)
744 pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n",
745 hc_attrs[i].kattr.attr.name, ret);
746 }
747 }
748
749 pr_info("MikroTik RouterBOARD hardware configuration sysfs driver v" RB_HARDCONFIG_VER "\n");
750
751 return 0;
752
753 fail:
754 kfree(hc_buf);
755 hc_buf = NULL;
756 return ret;
757 }
758
759 void __exit rb_hardconfig_exit(void)
760 {
761 kobject_put(hc_kobj);
762 kfree(hc_buf);
763 }