1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <fcntl.h>
5#include <sys/stat.h>
6#include <sys/mman.h>
7
8#include <bcmnvram.h>
9
10#include <rtconfig.h>
11#include <flash_mtd.h>
12#include <shutils.h>
13#include <shared.h>
14#include <plc_utils.h>
15
16#ifdef RTCONFIG_QCA
17#include <qca.h>
18#endif
19
20/*
21 * Convert PLC Key (e.g. NMK, DAK) string representation to binary data
22 * @param	a	string in xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx notation
23 * @param	e	binary data
24 * @return	TRUE if conversion was successful and FALSE otherwise
25 */
26int
27key_atoe(const char *a, unsigned char *e)
28{
29	char *c = (char *) a;
30	int i = 0;
31
32	memset(e, 0, PLC_KEY_LEN);
33	for (;;) {
34		e[i++] = (unsigned char) strtoul(c, &c, 16);
35		if (!*c++ || i == PLC_KEY_LEN)
36			break;
37	}
38	return (i == PLC_KEY_LEN);
39}
40
41/*
42 * Convert PLC Key binary data to string representation
43 * @param	e	binary data
44 * @param	a	string in xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx notation
45 * @return	a
46 */
47char *
48key_etoa(const unsigned char *e, char *a)
49{
50	char *c = a;
51	int i;
52
53	for (i = 0; i < PLC_KEY_LEN; i++) {
54		if (i)
55			*c++ = ':';
56		c += sprintf(c, "%02X", e[i] & 0xff);
57	}
58	return a;
59}
60
61/*
62 * increase n to mac last 24 bits and handle a carry problem
63 */
64static void inc_mac(unsigned char n, unsigned char *mac)
65{
66	int c = 0;
67
68	//dbg("MAC + %u\n", n);
69
70	if (mac[5] >= (0xff - n + 1))
71		c = 1;
72	else
73		c = 0;
74	mac[5] += n;
75
76	if (c == 1) {
77		if (mac[4] >= 0xff)
78			c = 1;
79		else
80			c = 0;
81		mac[4] += 1;
82
83		if (c == 1)
84			mac[3] += 1;
85	}
86}
87
88/*
89 * check PLC MAC/Key
90 * reference isValidMacAddr() in rc/ate.c
91 */
92int isValidPara(const char* str, int len)
93{
94	#define MULTICAST_BIT	0x0001
95	#define UNIQUE_OUI_BIT	0x0002
96	int sec_byte;
97	int i = 0, s = 0;
98
99	if (strlen(str) != ((len * 2) + len - 1))
100		return 0;
101
102	while (*str && i < (len * 2)) {
103		if (isxdigit(*str)) {
104			if (i == 1 && len == ETHER_ADDR_LEN) {
105				sec_byte = strtol(str, NULL, 16);
106				if ((sec_byte & MULTICAST_BIT) || (sec_byte & UNIQUE_OUI_BIT))
107					break;
108			}
109			i++;
110		}
111		else if (*str == ':') {
112			if (i == 0 || i/2-1 != s)
113				break;
114			++s;
115		}
116		++str;
117	}
118	return (i == (len * 2) && s == (len - 1));
119}
120
121static int __getPLC_para(char *ebuf, int addr)
122{
123	int len;
124
125	if (addr == OFFSET_PLC_MAC)
126		len = ETHER_ADDR_LEN;
127	else if (addr == OFFSET_PLC_NMK)
128		len = PLC_KEY_LEN;
129	else
130		return 0;
131
132	memset(ebuf, 0, sizeof(ebuf));
133
134	if (FRead(ebuf, addr, len) < 0) {
135		dbg("READ PLC parameter: Out of scope\n");
136		return 0;
137	}
138
139	return 1;
140}
141
142/*
143 * get PLC MAC from factory partition
144 */
145int getPLC_MAC(char *abuf)
146{
147	unsigned char ebuf[ETHER_ADDR_LEN];
148
149	if (__getPLC_para(ebuf, OFFSET_PLC_MAC)) {
150		memset(abuf, 0, sizeof(abuf));
151		if (ether_etoa(ebuf, abuf))
152			return 1;
153	}
154
155	return 0;
156}
157
158/*
159 * get PLC NMK from factory partition
160 */
161int getPLC_NMK(char *abuf)
162{
163	unsigned char ebuf[PLC_KEY_LEN];
164
165	if (__getPLC_para(ebuf, OFFSET_PLC_NMK)) {
166		memset(abuf, 0, sizeof(abuf));
167		if (key_etoa(ebuf, abuf))
168			return 1;
169	}
170
171	return 0;
172}
173
174/*
175 * ATE get PLC password from MAC
176 */
177static int __getPLC_PWD(unsigned char *emac, char *pwd)
178{
179	FILE *fp;
180	int len;
181	char cmd[64], buf[32];
182
183	inc_mac(2, emac);
184	sprintf(cmd, "/usr/local/bin/mac2pw -q %02x%02x%02x%02x%02x%02x", emac[0], emac[1], emac[2], emac[3], emac[4], emac[5]);
185	fp = popen(cmd, "r");
186	if (fp) {
187		len = fread(buf, 1, sizeof(buf), fp);
188		pclose(fp);
189		if (len > 1) {
190			buf[len - 1] = '\0';
191			strcpy(pwd, buf);
192		}
193		else
194			return 0;
195	}
196	else
197		return 0;
198
199	return 1;
200}
201
202int getPLC_PWD(void)
203{
204	unsigned char ebuf[ETHER_ADDR_LEN];
205	char pwd[32];
206
207	if (__getPLC_para(ebuf, OFFSET_PLC_MAC)) {
208		memset(pwd, 0, sizeof(pwd));
209		if (!__getPLC_PWD(ebuf, pwd))
210			return 0;
211
212		puts(pwd);
213	}
214	else
215		return 0;
216
217	return 1;
218}
219
220/*
221 * ATE get/set PLC parameter from/to factory partition, e.g. MAC, NMK
222 */
223int getPLC_para(int addr)
224{
225	char abuf[64], ebuf[16];
226
227
228	if (__getPLC_para(ebuf, addr)) {
229		memset(abuf, 0, sizeof(abuf));
230		if (addr == OFFSET_PLC_MAC)
231			ether_etoa(ebuf, abuf);
232		else
233			key_etoa(ebuf, abuf);
234		puts(abuf);
235	}
236	else
237		return 0;
238
239	return 1;
240}
241
242int setPLC_para(const char *abuf, int addr)
243{
244	unsigned char ebuf[32];
245	int len, ret;
246
247	if (abuf == NULL)
248		return 0;
249
250	memset(ebuf, 0, sizeof(ebuf));
251
252	if (addr == OFFSET_PLC_MAC) {
253		len = ETHER_ADDR_LEN;
254		if (!isValidPara(abuf, len))
255			return 0;
256
257		ret = ether_atoe(abuf, ebuf);
258	} else if (addr == OFFSET_PLC_NMK) {
259		len = PLC_KEY_LEN;
260		if (!isValidPara(abuf, len))
261			return 0;
262
263		ret = key_atoe(abuf, ebuf);
264	} else
265		return 0;
266
267	if (ret) {
268		FWrite(ebuf, addr, len);
269		getPLC_para(addr);
270		return 1;
271	}
272	else
273		return 0;
274}
275
276/*
277 * modify the value of specific offset
278 */
279static int modify_pib_byte(unsigned int offset, unsigned int value)
280{
281	return doSystem("/usr/local/bin/setpib -x %s 0x%x data %02x", BOOT_PIB_PATH, offset, value);
282}
283
284/*
285 * disable all LED event of PLC except Power event
286 */
287void ate_ctl_plc_led(void)
288{
289	int i = 0;
290
291	for (i = 0; i < 18; i++) {
292#if defined(RTCONFIG_AR7420)
293		if (i == 7) /* Power event */
294			continue;
295		modify_pib_byte((0x1B13 + (8 * i)), 0x1);
296#elif defined(RTCONFIG_QCA7500)
297		if (i == 11) /* Power event */
298			continue;
299		modify_pib_byte((0x21A7 + (8 * i)), 0x1);
300#endif
301	}
302}
303
304/*
305 * ATE turn on/off all LED of PLC
306 */
307int set_plc_all_led_onoff(int on)
308{
309	if (on) {
310#if defined(RTCONFIG_AR7420)
311		modify_pib_byte(0x1B49, 0x61);	/* GPIO 0, 5, 6 */
312#elif defined(RTCONFIG_QCA7500)
313		modify_pib_byte(0x21FD, 0xC0);	/* GPIO 6, 7 */
314		modify_pib_byte(0x21FE, 0x0);
315#endif
316	}
317	else {
318#if defined(RTCONFIG_AR7420)
319		modify_pib_byte(0x1B49, 0x0);
320#elif defined(RTCONFIG_QCA7500)
321		modify_pib_byte(0x21FD, 0x0);
322		modify_pib_byte(0x21FE, 0x02);	/* GPIO 9 */
323#endif
324	}
325
326	system("/usr/local/bin/plctool -i br0 -R -e > /dev/null");
327
328	return 0;
329}
330
331
332/*
333 * backup user's .nvm and .pib mechanism
334 */
335#define PLC_MAGIC		0x27051956	/* PLC Image Magic Number */
336#define DEFAULT_NVM_PATH	"/usr/local/bin/asus.nvm"
337#define DEFAULT_PIB_PATH	"/usr/local/bin/asus.pib"
338#define USER_NVM_PATH		"/tmp/user.nvm"
339#define USER_PIB_PATH		"/tmp/user.pib"
340#define HDR_PATH		"/tmp/plc.hdr"
341#define IMAGE_PATH		"/tmp/plc.img"
342#define RD_SIZE			65536
343
344#define PLC_MTD_NAME		"plc"
345#define PLC_MTD_DEV		"mtd5"
346
347#ifndef MAP_FAILED
348#define MAP_FAILED (-1)
349#endif
350
351typedef struct _plc_image_header {
352	unsigned int	magic;		/* PLC Image Header Magic Number */
353	unsigned int	hdr_crc;	/* PLC Image Header crc checksum */
354#if defined(PLN12)
355	unsigned int	nvm_size;	/* PLC .nvm size */
356	unsigned int	nvm_crc;	/* PLC .nvm crc checksum */
357#endif
358	unsigned int	pib_size;	/* PLC .pib size */
359	unsigned int	pib_crc;	/* PLC .pib crc checksum */
360} plc_image_header;
361
362plc_image_header header;
363
364/*
365 * check crc of header
366 *
367 * : input,  file name
368 *
369 * return
370 * 1: match
371 * 0: mismatch or fail
372 */
373static int hdr_match_crc(plc_image_header *hdr)
374{
375	unsigned int checksum, checksum_org;
376
377	checksum_org = hdr->hdr_crc;
378	hdr->hdr_crc = 0;
379	checksum = crc_calc(0, (const char *)hdr, sizeof(plc_image_header));
380	fprintf(stderr, "%s: crc %x/%x of header\n", __func__, checksum_org, checksum);
381
382	if (checksum != checksum_org) {
383		fprintf(stderr, "%s: header crc mismatch!\n", __func__);
384		return 0;
385	}
386
387	return 1;
388}
389
390/*
391 * get file length and crc
392 *
393 * fname: input,  file name
394 * len:   output, file length
395 * crc:   output, file crc
396 */
397static int get_crc(char* fname, unsigned int *len, unsigned int *crc)
398{
399	int fd, ret = -1;
400	struct stat fs;
401	unsigned char *ptr = NULL;
402
403	if ((fd = open(fname, O_RDONLY)) < 0) {
404		fprintf(stderr, "%s: Can't open %s\n", __func__, fname);
405		goto open_fail;
406	}
407
408	if (fstat(fd, &fs) < 0) {
409		fprintf(stderr, "%s: Can't stat %s\n", __func__, fname);
410		goto checkcrc_fail;
411	}
412	*len = fs.st_size;
413
414	ptr = (unsigned char *)mmap(0, fs.st_size, PROT_READ, MAP_SHARED, fd, 0);
415	if (ptr == (unsigned char *)MAP_FAILED) {
416		fprintf(stderr, "%s: Can't map %s\n", __func__, fname);
417		goto checkcrc_fail;
418	}
419	*crc = crc_calc(0, (const char *)ptr, fs.st_size);
420
421	ret = 0;
422
423checkcrc_fail:
424	if (ptr != NULL)
425		munmap(ptr, fs.st_size);
426#if defined(_POSIX_SYNCHRONIZED_IO) && !defined(__sun__) && !defined(__FreeBSD__)
427	(void)fdatasync(fd);
428#else
429	(void)fsync(fd);
430#endif
431	close(fd);
432
433open_fail:
434	return ret;
435}
436
437/*
438 * check crc of file and header record
439 *
440 * fname: input,  file name
441 * crc:   input,  header record
442 *
443 * return
444 * 1: match
445 * 0: mismatch or fail
446 */
447static int match_crc(char *fname, unsigned int crc)
448{
449	unsigned int len, checksum;
450
451	if (get_crc(fname, &len, &checksum)) {
452		fprintf(stderr, "%s: Can't check crc of %s\n", __func__, fname);
453		return 0;
454	}
455	fprintf(stderr, "%s: crc %x/%x of %s\n", __func__, crc, checksum, fname);
456
457	if (checksum != crc) {
458		fprintf(stderr, "%s: %s crc mismatch!\n", __func__, fname);
459		return 0;
460	}
461
462	return 1;
463}
464
465/*
466 * read .nvm and .pib from flash
467 */
468#if defined(PLN12)
469static int plc_read_from_flash(char *nvm_path, char *pib_path)
470#else
471static int plc_read_from_flash(char *pib_path)
472#endif
473{
474	int rfd, wfd, ret = -1;
475	unsigned int rlen, wlen;
476	plc_image_header *hdr = &header;
477	char cmd[32], buf[RD_SIZE];
478
479	sprintf(cmd, "cat /dev/%s > %s", PLC_MTD_DEV, IMAGE_PATH);
480	system(cmd);
481
482	memset(hdr, 0, sizeof(plc_image_header));
483	// header
484	if ((rfd = open(IMAGE_PATH, O_RDONLY)) < 0) {
485		fprintf(stderr, "%s: Can't open %s\n", __func__, IMAGE_PATH);
486		goto open_fail;
487	}
488	rlen = read(rfd, hdr, sizeof(plc_image_header));
489	// check header crc and magic number
490	if (hdr_match_crc(hdr) == 0 || hdr->magic != PLC_MAGIC)
491		goto mismatch;
492
493	memset(buf, 0, sizeof(buf));
494#if defined(PLN12)
495	// .nvm
496	wlen = hdr->nvm_size;
497	if ((wfd = open(nvm_path, O_RDWR|O_CREAT, 0666)) < 0) {
498		fprintf(stderr, "%s: Can't open %s\n", __func__, nvm_path);
499		goto mismatch;
500	}
501	while (wlen > 0) {
502		if (wlen > RD_SIZE)
503			rlen = read(rfd, buf, RD_SIZE);
504		else
505			rlen = read(rfd, buf, wlen);
506		write(wfd, buf, rlen);
507		wlen -= rlen;
508	}
509	close(wfd);
510	// check .nvm crc
511	if (match_crc(nvm_path, hdr->nvm_crc) == 0)
512		goto mismatch;
513#endif
514
515	// .pib
516	wlen = hdr->pib_size;
517	if ((wfd = open(pib_path, O_RDWR|O_CREAT, 0666)) < 0) {
518		fprintf(stderr, "%s: Can't open %s\n", __func__, pib_path);
519		goto mismatch;
520	}
521	while (wlen > 0) {
522		if (wlen > RD_SIZE)
523			rlen = read(rfd, buf, RD_SIZE);
524		else
525			rlen = read(rfd, buf, wlen);
526		write(wfd, buf, rlen);
527		wlen -= rlen;
528	}
529	close(wfd);
530	// check .pib crc
531	if (match_crc(pib_path, hdr->pib_crc) == 0)
532		goto mismatch;
533
534	ret = 0;
535
536mismatch:
537	close(rfd);
538
539open_fail:
540	unlink(IMAGE_PATH);
541
542	return ret;
543}
544
545/*
546 * write .nvm and .pib to flash
547 */
548#if defined(PLN12)
549static int plc_write_to_flash(char *nvm_path, char *pib_path)
550#else
551static int plc_write_to_flash(char *pib_path)
552#endif
553{
554	int fd, fl;
555	plc_image_header *hdr = &header;
556	char cmd[128];
557
558	memset(hdr, 0, sizeof(plc_image_header));
559	hdr->magic = PLC_MAGIC;
560
561#if defined(PLN12)
562	// get length and crc of .nvm
563	if (get_crc(nvm_path, &hdr->nvm_size, &hdr->nvm_crc)) {
564		fprintf(stderr, "%s: Can't check crc of %s\n", __func__, nvm_path);
565		return -1;
566	}
567#endif
568
569	// get length and crc of .pib
570	if (get_crc(pib_path, &hdr->pib_size, &hdr->pib_crc)) {
571		fprintf(stderr, "%s: Can't check crc of %s\n", __func__, pib_path);
572		return -1;
573	}
574
575	// create header
576	hdr->hdr_crc = crc_calc(0, (const char *)hdr, sizeof(plc_image_header));
577	if ((fd = open(HDR_PATH, O_RDWR|O_CREAT, 0666)) < 0) {
578		fprintf(stderr, "%s: Can't open %s\n", __func__, HDR_PATH);
579		return -1;
580	}
581	write(fd, hdr, sizeof(plc_image_header));
582	close(fd);
583
584	// write to plc partition
585	while (1) {
586		if ((fl = open(PLC_LOCK_FILE, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600)) >= 0) {
587#if defined(PLN12)
588			sprintf(cmd, "cat %s %s %s > %s", HDR_PATH, nvm_path, pib_path, IMAGE_PATH);
589#else
590			sprintf(cmd, "cat %s %s > %s", HDR_PATH, pib_path, IMAGE_PATH);
591#endif
592			system(cmd);
593			sprintf(cmd, "mtd-write -i %s -d %s", IMAGE_PATH, PLC_MTD_NAME);
594			system(cmd);
595			close(PLC_LOCK_FILE);
596			unlink(PLC_LOCK_FILE);
597			break;
598		}
599		else {
600			dbg("%s: PLC file lock! Try again after waiting 1 sec\n", __func__);
601			sleep(1);
602		}
603	}
604
605	unlink(HDR_PATH);
606	unlink(IMAGE_PATH);
607
608	return 0;
609}
610
611/*
612 * write default .nvm and .pib to flash
613 */
614int default_plc_write_to_flash(void)
615{
616	FILE *fp;
617	int len, i;
618	char cmd[64], buf[64];
619	char mac[18], dak[48], nmk[48];
620	unsigned char emac[ETHER_ADDR_LEN], enmk[PLC_KEY_LEN];
621
622#if defined(PLN12)
623	doSystem("cp %s %s", DEFAULT_NVM_PATH, BOOT_NVM_PATH);
624#endif
625	doSystem("cp %s %s", DEFAULT_PIB_PATH, BOOT_PIB_PATH);
626
627	// modify .pib
628	// MAC
629	if (!__getPLC_para(emac, OFFSET_PLC_MAC)) {
630		_dprintf("READ PLC MAC: Out of scope\n");
631	}
632	else {
633		if (emac[0] != 0xff) {
634			if (ether_etoa(emac, mac))
635				doSystem("/usr/local/bin/modpib %s -M %s", BOOT_PIB_PATH, mac);
636
637			// DAK
638			if (__getPLC_PWD(emac, buf)) {
639				sprintf(cmd, "/usr/local/bin/hpavkey -D %s", buf);
640				fp = popen(cmd, "r");
641				if (fp) {
642					len = fread(buf, 1, sizeof(buf), fp);
643					pclose(fp);
644					if (len > 1) {
645						buf[len - 1] = '\0';
646
647						for (i = 0; i < PLC_KEY_LEN; i++) {
648							if (i == 0)
649								sprintf(dak, "%c%c", buf[0], buf[1]);
650							else
651								sprintf(dak, "%s:%c%c", dak, buf[i*2], buf[i*2+1]);
652						}
653						doSystem("/usr/local/bin/modpib %s -D %s", BOOT_PIB_PATH, dak);
654					}
655				}
656			}
657		}
658	}
659
660	// NMK
661	if (!__getPLC_para(enmk, OFFSET_PLC_NMK))
662		_dprintf("READ PLC NMK: Out of scope\n");
663	else {
664		if (enmk[0] != 0xff && enmk[1] != 0xff && enmk[2] != 0xff) {
665			if (key_etoa(enmk, nmk))
666				doSystem("/usr/local/bin/modpib %s -N %s", BOOT_PIB_PATH, nmk);
667		}
668	}
669
670#if defined(PLN12)
671	return plc_write_to_flash(BOOT_NVM_PATH, BOOT_PIB_PATH);
672#else
673	return plc_write_to_flash(BOOT_PIB_PATH);
674#endif
675}
676
677/*
678 * write default .nvm and .pib to flash, if plc partition is empty.
679 * reload .nvm and .pib to /tmp from flash for plchost utility
680 */
681int load_plc_setting(void)
682{
683#if defined(PLN12)
684	if (plc_read_from_flash(BOOT_NVM_PATH, BOOT_PIB_PATH))
685#else
686	if (plc_read_from_flash(BOOT_PIB_PATH))
687#endif
688		return default_plc_write_to_flash();
689	return 0;
690}
691
692/*
693 * set plc_flag nvram for save_plc_setting() function
694 *
695 * because of plc-utils/plc/plchost.c cannot include bcmnvram.h
696 * 	error: conflicting types for 'bool' from
697 * 		src-qca/include/typedefs.h
698 * 		plc-utils/tools/types.h
699 */
700void set_plc_flag(int flag)
701{
702	nvram_set_int("plc_flag", flag);
703}
704
705/*
706 * write user .nvm or .pib to flash
707 * case1: backup .nvm
708 * case2: backup .pib
709 * case3: backup .nvm and .pib
710 */
711void save_plc_setting(void)
712{
713	switch (atoi(nvram_safe_get("plc_flag"))) {
714	case 2:
715		_dprintf("sleep 10 second for wait pairing done!\n");
716		sleep(10);
717#if defined(PLN12)
718		plc_write_to_flash(BOOT_NVM_PATH, USER_PIB_PATH);
719#else
720		plc_write_to_flash(USER_PIB_PATH);
721#endif
722		break;
723#if defined(PLN12)
724	case 1:
725		plc_write_to_flash(USER_NVM_PATH, BOOT_PIB_PATH);
726		break;
727	case 3:
728		plc_write_to_flash(USER_NVM_PATH, USER_PIB_PATH);
729		break;
730#endif
731	default:
732		fprintf(stderr, "%s: wrong flag!", __func__);
733	}
734
735	set_plc_flag(-1);
736}
737
738void turn_led_pwr_off(void)
739{
740	if (!nvram_match("asus_mfg", "0"))
741		return;
742
743	nvram_set("plc_ready", "1");
744
745#if (defined(PLN12) || defined(PLAC56))
746	led_control(LED_POWER_RED, LED_OFF);
747#endif
748}
749