1 /*
2 * The driver of SUNXI NET MAC ADDR Manager.
3 *
4 * Copyright (C) 2013 Allwinner.
5 *
6 * This file is licensed under the terms of the GNU General Public
7 * License version 2. This program is licensed "as is" without any
8 * warranty of any kind, whether express or implied.
9 */
10 #define DEBUG
11
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/miscdevice.h>
15 #include <linux/of.h>
16 #include <linux/platform_device.h>
17 #include <linux/random.h>
18
19 #define ADDR_MGT_DBG(fmt, arg...) printk(KERN_DEBUG "[ADDR_MGT] %s: " fmt "\n",\
20 __func__, ## arg)
21 #define ADDR_MGT_ERR(fmt, arg...) printk(KERN_ERR "[ADDR_MGT] %s: " fmt "\n",\
22 __func__, ## arg)
23
24 #define MODULE_CUR_VERSION "v1.0.11"
25
26 #define MATCH_STR_LEN 20
27 #define ADDR_VAL_LEN 6
28 #define ADDR_STR_LEN 18
29 #define ID_LEN 16
30 #define HASH_LEN 32
31
32 #define TYPE_ANY 0
33 #define TYPE_BURN 1
34 #define TYPE_IDGEN 2
35 #define TYPE_USER 3
36 #define TYPE_RAND 4
37
38 #define ADDR_FMT_STR 0
39 #define ADDR_FMT_VAL 1
40
41 #define IS_TYPE_INVALID(x) ((x < TYPE_ANY) || (x > TYPE_RAND))
42
43 #define ADDR_CLASS_ATTR_ADD(name) \
44 static ssize_t addr_##name##_show(struct class *class, \
45 struct class_attribute *attr, char *buffer) \
46 { \
47 char addr[ADDR_STR_LEN]; \
48 if (IS_TYPE_INVALID(get_addr_by_name(ADDR_FMT_STR, addr, #name))) \
49 return 0; \
50 return sprintf(buffer, "%.17s\n", addr); \
51 } \
52 static ssize_t addr_##name##_store(struct class *class, \
53 struct class_attribute *attr, \
54 const char *buffer, size_t count) \
55 { \
56 if (count != ADDR_STR_LEN) { \
57 ADDR_MGT_ERR("Length wrong."); \
58 return -EINVAL; \
59 } \
60 set_addr_by_name(TYPE_USER, ADDR_FMT_STR, buffer, #name); \
61 return count; \
62 } \
63 static CLASS_ATTR_RW(addr_##name);
64
65 struct addr_mgt_info {
66 unsigned int type_def;
67 unsigned int type_cur;
68 unsigned int flag;
69 char *addr;
70 char *name;
71 };
72
73 static struct addr_mgt_info info[] = {
74 {TYPE_ANY, TYPE_ANY, 1, NULL, "wifi"},
75 {TYPE_ANY, TYPE_ANY, 0, NULL, "bt" },
76 {TYPE_ANY, TYPE_ANY, 1, NULL, "eth" },
77 };
78
79 extern int hmac_sha256(const uint8_t *plaintext, ssize_t psize, uint8_t *output);
80
81 #if IS_ENABLED(CONFIG_AW_SID)
82 #include <sunxi-sid.h>
83 #else
sunxi_get_soc_chipid(uint8_t * id)84 static int sunxi_get_soc_chipid(uint8_t *id)
85 {
86 (void)id;
87 return -1;
88 }
89 #endif
90
addr_parse(int fmt,const char * addr,int check)91 static int addr_parse(int fmt, const char *addr, int check)
92 {
93 char val_buf[ADDR_VAL_LEN];
94 char cmp_buf[ADDR_VAL_LEN];
95 int ret = ADDR_VAL_LEN;
96
97 if (fmt == ADDR_FMT_STR)
98 ret = sscanf(addr, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
99 &val_buf[0], &val_buf[1], &val_buf[2],
100 &val_buf[3], &val_buf[4], &val_buf[5]);
101 else
102 memcpy(val_buf, addr, ADDR_VAL_LEN);
103
104 if (ret != ADDR_VAL_LEN)
105 return -1;
106
107 if (check && (val_buf[0] & 0x1))
108 return -1;
109
110 memset(cmp_buf, 0x00, ADDR_VAL_LEN);
111 if (memcmp(val_buf, cmp_buf, ADDR_VAL_LEN) == 0)
112 return -1;
113
114 memset(cmp_buf, 0xFF, ADDR_VAL_LEN);
115 if (memcmp(val_buf, cmp_buf, ADDR_VAL_LEN) == 0)
116 return -1;
117
118 return 0;
119 }
120
addr_find_by_name(char * name)121 static struct addr_mgt_info *addr_find_by_name(char *name)
122 {
123 int i = 0;
124 for (i = 0; i < ARRAY_SIZE(info); i++) {
125 if (strcmp(info[i].name, name) == 0)
126 return &info[i];
127 }
128 return NULL;
129 }
130
get_addr_by_name(int fmt,char * addr,char * name)131 static int get_addr_by_name(int fmt, char *addr, char *name)
132 {
133 struct addr_mgt_info *t;
134
135 t = addr_find_by_name(name);
136 if (t == NULL) {
137 ADDR_MGT_ERR("can't find addr named: %s", name);
138 return -1;
139 }
140
141 if (IS_TYPE_INVALID(t->type_cur)) {
142 ADDR_MGT_ERR("addr type invalid");
143 return -1;
144 }
145
146 if (addr_parse(ADDR_FMT_VAL, t->addr, t->flag)) {
147 ADDR_MGT_ERR("addr parse fail(%s)", t->addr);
148 return -1;
149 }
150
151 if (fmt == ADDR_FMT_STR)
152 sprintf(addr, "%02X:%02X:%02X:%02X:%02X:%02X",
153 t->addr[0], t->addr[1], t->addr[2],
154 t->addr[3], t->addr[4], t->addr[5]);
155 else
156 memcpy(addr, t->addr, ADDR_VAL_LEN);
157
158 return t->type_cur;
159 }
160
set_addr_by_name(int type,int fmt,const char * addr,char * name)161 static int set_addr_by_name(int type, int fmt, const char *addr, char *name)
162 {
163 struct addr_mgt_info *t;
164
165 t = addr_find_by_name(name);
166 if (t == NULL) {
167 ADDR_MGT_ERR("can't find addr named: %s", name);
168 return -1;
169 }
170
171 if (addr_parse(fmt, addr, t->flag)) {
172 ADDR_MGT_ERR("addr parse fail(%s)", addr);
173 return -1;
174 }
175
176 t->type_cur = type;
177 if (fmt == ADDR_FMT_STR)
178 sscanf(addr, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
179 &t->addr[0], &t->addr[1], &t->addr[2],
180 &t->addr[3], &t->addr[4], &t->addr[5]);
181 else
182 memcpy(t->addr, addr, ADDR_VAL_LEN);
183
184 return 0;
185 }
186
get_custom_mac_address(int fmt,char * name,char * addr)187 int get_custom_mac_address(int fmt, char *name, char *addr)
188 {
189 return get_addr_by_name(fmt, addr, name);
190 }
191 EXPORT_SYMBOL_GPL(get_custom_mac_address);
192
set_wifi_custom_mac_address(char * addr_str,size_t count)193 int set_wifi_custom_mac_address(char *addr_str, size_t count)
194 {
195 if ((count > ADDR_STR_LEN) || addr_parse(ADDR_FMT_STR, addr_str, 1)) {
196 ADDR_MGT_ERR("Format wrong.");
197 return -EINVAL;
198 }
199
200 return set_addr_by_name(TYPE_USER, ADDR_FMT_STR, addr_str, "wifi");
201 }
202 EXPORT_SYMBOL_GPL(set_wifi_custom_mac_address);
203
addr_factory(struct device_node * np,int idx,int type,char * mac,char * name)204 static int addr_factory(struct device_node *np,
205 int idx, int type, char *mac, char *name)
206 {
207 int ret;
208 char match[MATCH_STR_LEN];
209 const char *p;
210 char id[ID_LEN], cmp_buf[ID_LEN];
211 static char hash[HASH_LEN];
212 static int initial = -1;
213
214 switch (type) {
215 case TYPE_BURN:
216 sprintf(match, "addr_%s", name);
217 ret = of_property_read_string_index(np, match, 0, &p);
218 if (ret)
219 return -1;
220
221 ret = sscanf(p, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
222 &mac[0], &mac[1], &mac[2],
223 &mac[3], &mac[4], &mac[5]);
224
225 if (ret != ADDR_VAL_LEN)
226 return -1;
227 break;
228 case TYPE_IDGEN:
229 if (idx > HASH_LEN / ADDR_VAL_LEN - 1)
230 return -1;
231 if (initial == -1) {
232 if (sunxi_get_soc_chipid(id)) {
233 initial = 0;
234 return -1;
235 }
236 memset(cmp_buf, 0x00, ID_LEN);
237 if (memcmp(id, cmp_buf, ID_LEN) == 0) {
238 initial = 0;
239 return -1;
240 }
241 memset(cmp_buf, 0xFF, ID_LEN);
242 if (memcmp(id, cmp_buf, ID_LEN) == 0) {
243 initial = 0;
244 return -1;
245 }
246 if (hmac_sha256(id, ID_LEN, hash)) {
247 initial = 0;
248 return -1;
249 }
250 initial = 1;
251 }
252
253 if (initial == 0)
254 return -1;
255
256 memcpy(mac, &hash[idx * ADDR_VAL_LEN], ADDR_VAL_LEN);
257 break;
258 case TYPE_RAND:
259 get_random_bytes(mac, ADDR_VAL_LEN);
260 break;
261 default:
262 ADDR_MGT_ERR("unsupport type: %d", type);
263 return -1;
264 }
265 return 0;
266 }
267
addr_init(struct platform_device * pdev)268 static int addr_init(struct platform_device *pdev)
269 {
270 struct device_node *np = pdev->dev.of_node;
271 int type, i, j;
272 char match[MATCH_STR_LEN];
273 char addr[ADDR_VAL_LEN];
274 int type_tab[] = {TYPE_BURN, TYPE_IDGEN, TYPE_RAND};
275
276 /* init addr type and value */
277 for (i = 0; i < ARRAY_SIZE(info); i++) {
278 sprintf(match, "type_addr_%s", info[i].name);
279 if (of_property_read_u32(np, match, &type)) {
280 ADDR_MGT_DBG("Failed to get type_def_%s, use default: %d",
281 info[i].name, info[i].type_def);
282 } else {
283 info[i].type_def = type;
284 info[i].type_cur = type;
285 }
286
287 if (IS_TYPE_INVALID(info[i].type_def))
288 return -1;
289 if (info[i].type_def != TYPE_ANY) {
290 if (addr_factory(np, i, info[i].type_def, addr, info[i].name))
291 return -1;
292 } else {
293 for (j = 0; j < ARRAY_SIZE(type_tab); j++) {
294 if (!addr_factory(np, i, type_tab[j], addr, info[i].name)) {
295 info[i].type_cur = type_tab[j];
296 break;
297 }
298 }
299 }
300
301 if (info[i].flag)
302 addr[0] &= 0xFC;
303
304 if (addr_parse(ADDR_FMT_VAL, addr, info[i].flag))
305 return -1;
306 else {
307 info[i].addr = devm_kzalloc(&pdev->dev, ADDR_VAL_LEN, GFP_KERNEL);
308 memcpy(info[i].addr, addr, ADDR_VAL_LEN);
309 }
310 }
311 return 0;
312 }
313
summary_show(struct class * class,struct class_attribute * attr,char * buffer)314 static ssize_t summary_show(struct class *class,
315 struct class_attribute *attr, char *buffer)
316 {
317 int i = 0, ret = 0;
318
319 ret += sprintf(&buffer[ret], "name cfg cur address\n");
320 for (i = 0; i < ARRAY_SIZE(info); i++) {
321 ret += sprintf(&buffer[ret],
322 "%4s %d %d %02X:%02X:%02X:%02X:%02X:%02X\n",
323 info[i].name, info[i].type_def, info[i].type_cur,
324 info[i].addr[0], info[i].addr[1], info[i].addr[2],
325 info[i].addr[3], info[i].addr[4], info[i].addr[5]);
326 }
327 return ret;
328 }
329 static CLASS_ATTR_RO(summary);
330
331 ADDR_CLASS_ATTR_ADD(wifi);
332 ADDR_CLASS_ATTR_ADD(bt);
333 ADDR_CLASS_ATTR_ADD(eth);
334
335 static struct attribute *addr_class_attrs[] = {
336 &class_attr_summary.attr,
337 &class_attr_addr_wifi.attr,
338 &class_attr_addr_bt.attr,
339 &class_attr_addr_eth.attr,
340 NULL
341 };
342 ATTRIBUTE_GROUPS(addr_class);
343
344 static struct class addr_class = {
345 .name = "addr_mgt",
346 .owner = THIS_MODULE,
347 .class_groups = addr_class_groups,
348 };
349
350 static const struct of_device_id addr_mgt_ids[] = {
351 { .compatible = "allwinner,sunxi-addr_mgt" },
352 { /* Sentinel */ }
353 };
354
addr_mgt_probe(struct platform_device * pdev)355 static int addr_mgt_probe(struct platform_device *pdev)
356 {
357 int status;
358
359 ADDR_MGT_DBG("module version: %s", MODULE_CUR_VERSION);
360 status = class_register(&addr_class);
361 if (status < 0) {
362 ADDR_MGT_ERR("class register error, status: %d.", status);
363 return -1;
364 }
365
366 if (addr_init(pdev)) {
367 ADDR_MGT_ERR("failed to init addr.");
368 class_unregister(&addr_class);
369 return -1;
370 }
371 ADDR_MGT_DBG("success.");
372 return 0;
373 }
374
addr_mgt_remove(struct platform_device * pdev)375 static int addr_mgt_remove(struct platform_device *pdev)
376 {
377 class_unregister(&addr_class);
378 return 0;
379 }
380
381 static struct platform_driver addr_mgt_driver = {
382 .probe = addr_mgt_probe,
383 .remove = addr_mgt_remove,
384 .driver = {
385 .owner = THIS_MODULE,
386 .name = "sunxi-addr-mgt",
387 .of_match_table = addr_mgt_ids,
388 },
389 };
390
391 module_platform_driver_probe(addr_mgt_driver, addr_mgt_probe);
392
393 MODULE_AUTHOR("Allwinnertech");
394 MODULE_DESCRIPTION("Network MAC Addess Manager");
395 MODULE_LICENSE("GPL");
396