a03aa21b4d55ff7a54f9693c99cad2b0d95bd1d7
[oweals/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 }