• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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