1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <unistd.h>
5#include <sys/types.h>
6#include <sys/stat.h>
7#include <fcntl.h>
8#include <bcmnvram.h>
9#include <bcmdevs.h>
10#include <sys/ioctl.h>
11#include <net/if.h>
12#include <sys/socket.h>
13#include <linux/sockios.h>
14#include <wlutils.h>
15#include <linux_gpio.h>
16#include <etioctl.h>
17#include "utils.h"
18#include "shutils.h"
19#include "shared.h"
20#include <trxhdr.h>
21#include <bcmutils.h>
22#include <bcmendian.h>
23
24#ifdef RTCONFIG_QTN
25#include "web-qtn.h"
26#endif
27
28uint32_t gpio_dir(uint32_t gpio, int dir)
29{
30	/* FIXME
31	return bcmgpio_connect(gpio, dir);
32	 */
33
34	return 0;
35}
36
37#define swapportstatus(x) \
38{ \
39    unsigned int data = *(unsigned int*)&(x); \
40    data = ((data & 0x000c0000) >> 18) |    \
41           ((data & 0x00030000) >> 14) |    \
42           ((data & 0x0000c000) >> 10) |    \
43           ((data & 0x00003000) >>  6) |    \
44	   ((data & 0x00000c00) >>  2);     \
45    *(unsigned int*)&(x) = data;            \
46}
47
48extern uint32_t gpio_read(void);
49extern void gpio_write(uint32_t bitvalue, int en);
50
51uint32_t get_gpio(uint32_t gpio)
52{
53	uint32_t bit_value;
54	uint32_t bit_mask;
55
56	bit_mask = 1 << gpio;
57	bit_value = gpio_read()&bit_mask;
58
59	return bit_value == 0 ? 0 : 1;
60}
61
62
63uint32_t set_gpio(uint32_t gpio, uint32_t value)
64{
65/*
66	uint32_t bit;
67	bit = 1<< gpio;
68
69	_dprintf("set_gpio: %d %d\n", bit, value);
70	gpio_write(bit, value);
71*/
72	//_dprintf("set_gpio: %d %d\n", gpio, value);
73	gpio_write(gpio, value);
74
75	return 0;
76}
77
78#ifdef RTCONFIG_BCMFA
79int get_fa_rev(void)
80{
81	int fd, ret;
82	unsigned int rev;
83	struct ifreq ifr;
84	et_var_t var;
85
86	fd = socket(AF_INET, SOCK_DGRAM, 0);
87	if (fd < 0) goto skip;
88
89	rev = 0;
90	var.set = 0;
91	var.cmd = IOV_FA_REV;
92	var.buf = &rev;
93	var.len = sizeof(rev);
94
95	memset(&ifr, 0, sizeof(ifr));
96	strcpy(ifr.ifr_name, "eth0");
97	ifr.ifr_data = (caddr_t) &var;
98
99	ret = ioctl(fd, SIOCSETGETVAR, (caddr_t)&ifr);
100	close(fd);
101	if (ret < 0)
102		goto skip;
103
104	return rev;
105
106skip:
107	return 0;
108}
109
110int get_fa_dump(void)
111{
112	int fd, rev, ret;
113	struct ifreq ifr;
114	et_var_t var;
115
116	fd = socket(AF_INET, SOCK_DGRAM, 0);
117	if (fd < 0) goto skip;
118
119	rev = 0;
120	var.set = 0;
121	var.cmd = IOV_FA_DUMP;
122	var.buf = &rev;
123	var.len = sizeof(rev);
124
125	memset(&ifr, 0, sizeof(ifr));
126	strcpy(ifr.ifr_name, "eth0");
127	ifr.ifr_data = (caddr_t) &var;
128
129	ret = ioctl(fd, SIOCSETGETVAR, (caddr_t)&ifr);
130	close(fd);
131	if (ret < 0)
132		goto skip;
133
134	return rev;
135
136skip:
137	return 0;
138}
139
140#endif
141
142int get_switch_model(void)
143{
144	int fd, devid, ret;
145	struct ifreq ifr;
146	et_var_t var;
147
148	fd = socket(AF_INET, SOCK_DGRAM, 0);
149	if (fd < 0) goto skip;
150
151	devid = 0;
152	var.set = 0;
153	var.cmd = IOV_ET_ROBO_DEVID;
154	var.buf = &devid;
155	var.len = sizeof(devid);
156
157	memset(&ifr, 0, sizeof(ifr));
158	strcpy(ifr.ifr_name, "eth0"); // is it always the same?
159	ifr.ifr_data = (caddr_t) &var;
160
161	ret = ioctl(fd, SIOCSETGETVAR, (caddr_t)&ifr);
162	close(fd);
163	if (ret < 0)
164		goto skip;
165	if (devid == 0x25)
166		return SWITCH_BCM5325;
167	else if (devid == 0x3115)
168		return SWITCH_BCM53115;
169	else if (devid == 0x3125)
170		return SWITCH_BCM53125;
171	else if ((devid & 0xfffffff0) == 0x53010)
172		return SWITCH_BCM5301x;
173
174skip:
175	return SWITCH_UNKNOWN;
176}
177
178int robo_ioctl(int fd, int write, int page, int reg, uint32_t *value)
179{
180	static int __ioctl_args[2] = { SIOCGETCROBORD, SIOCSETCROBOWR };
181	struct ifreq ifr;
182	int ret, vecarg[4];
183
184	memset(&ifr, 0, sizeof(ifr));
185	strcpy(ifr.ifr_name, "eth0"); // is it always the same?
186	ifr.ifr_data = (caddr_t) vecarg;
187
188	vecarg[0] = (page << 16) | reg;
189#if defined(BCM5301X) || defined(RTAC1200G) || defined(RTAC1200GP)
190	vecarg[1] = 0;
191	vecarg[2] = *value;
192#else
193	vecarg[1] = *value;
194#endif
195	ret = ioctl(fd, __ioctl_args[write], (caddr_t)&ifr);
196
197#if defined(BCM5301X) || defined(RTAC1200G) || defined(RTAC1200GP)
198	*value = vecarg[2];
199#else
200	*value = vecarg[1];
201#endif
202
203	return ret;
204}
205
206int phy_ioctl(int fd, int write, int phy, int reg, uint32_t *value)
207{
208#ifndef BCM5301X
209	static int __ioctl_args[2] = { SIOCGETCPHYRD2, SIOCSETCPHYWR2 };
210	struct ifreq ifr;
211	int ret, vecarg[2];
212
213	memset(&ifr, 0, sizeof(ifr));
214	strcpy(ifr.ifr_name, "eth0"); // is it always the same?
215	ifr.ifr_data = (caddr_t) vecarg;
216
217	vecarg[0] = (phy << 16) | reg;
218	vecarg[1] = *value;
219	ret = ioctl(fd, __ioctl_args[write], (caddr_t)&ifr);
220
221	*value = vecarg[1];
222
223	return ret;
224#else
225	return robo_ioctl(fd, write, 0x10 + phy, reg, value);
226#endif
227}
228
229// !0: connected
230//  0: disconnected
231uint32_t get_phy_status(uint32_t portmask)
232{
233	int fd, model;
234	uint32_t value, mask = 0;
235#ifndef BCM5301X
236	int i;
237#endif
238
239	model = get_switch();
240	if (model == SWITCH_UNKNOWN) return 0;
241
242	fd = socket(AF_INET, SOCK_DGRAM, 0);
243	if (fd < 0) return 0;
244
245	switch (model) {
246#ifndef BCM5301X
247	case SWITCH_BCM53125:
248#ifdef RTCONFIG_LANWAN_LED
249		/* N15U can't read link status from phy sometimes */
250		if (get_model() == MODEL_RTN15U)
251			goto case_SWITCH_ROBORD;
252		/* fall through */
253#endif
254	case SWITCH_BCM53115:
255	case SWITCH_BCM5325:
256		for (i = 0; i < 5 && (portmask >> i); i++) {
257			if ((portmask & (1U << i)) == 0)
258				continue;
259
260			if (phy_ioctl(fd, 0, i, 0x01, &value) < 0)
261				continue;
262			/* link is down, but negotiation has started
263			 * read register again, use previous value, if failed */
264			if ((value & 0x22) == 0x20)
265				phy_ioctl(fd, 0, i, 0x01, &value);
266
267			if (value & (1U << 2))
268				mask |= (1U << i);
269		}
270		break;
271#ifdef RTCONFIG_LANWAN_LED
272	case_SWITCH_ROBORD:
273		/* fall through */
274#endif
275#endif
276	case SWITCH_BCM5301x:
277		if (robo_ioctl(fd, 0, 0x01, 0x00, &value) < 0)
278			_dprintf("et ioctl SIOCGETCROBORD failed!\n");
279		mask = value & portmask & 0x1f;
280		break;
281	}
282	close(fd);
283
284	//_dprintf("# get_phy_status %x %x\n", mask, portmask);
285
286	return mask;
287}
288
289// 2bit per port (0-4(5)*2 shift)
290// 0: 10 Mbps
291// 1: 100 Mbps
292// 2: 1000 Mbps
293uint32_t get_phy_speed(uint32_t portmask)
294{
295	int fd, model;
296	uint32_t value, mask = 0;
297
298	model = get_switch();
299	if (model == SWITCH_UNKNOWN) return 0;
300
301	fd = socket(AF_INET, SOCK_DGRAM, 0);
302	if (fd < 0) return 0;
303
304	if (robo_ioctl(fd, 0, 0x01, 0x04, &value) < 0)
305		value = 0;
306	close(fd);
307
308	switch (model) {
309#ifndef BCM5301X
310	case SWITCH_BCM5325:
311		/* 5325E/535x, 1bit: 0=10 Mbps, 1=100Mbps */
312		for (mask = 0; value & 0x1f; value >>= 1) {
313			mask |= (value & 0x01);
314			mask <<= 2;
315		}
316		swapportstatus(mask);
317		break;
318	case SWITCH_BCM53115:
319	case SWITCH_BCM53125:
320		/* fall through */
321#endif
322	case SWITCH_BCM5301x:
323		/* 5301x/53115/53125, 2bit:00=10 Mbps,01=100Mbps,10=1000Mbps */
324		mask = value & portmask & 0x3ff;
325		break;
326	}
327
328	//_dprintf("get_phy_speed %x %x\n", vecarg[1], portmask);
329
330	return mask;
331}
332
333uint32_t set_phy_ctrl(uint32_t portmask, int ctrl)
334{
335	int fd, i, model;
336	uint32_t value;
337
338	model = get_switch();
339	if (model == SWITCH_UNKNOWN) return 0;
340
341	fd = socket(AF_INET, SOCK_DGRAM, 0);
342	if (fd < 0) return 0;
343
344	for (i = 0; i < 5 && (portmask >> i); i++) {
345		if ((portmask & (1U << i)) == 0)
346			continue;
347
348		switch (model) {
349#ifndef BCM5301X
350		case SWITCH_BCM5325:
351			if (phy_ioctl(fd, 0, i, 0x1e, &value) < 0)
352				value = 0;
353			value &= 0x0007;
354			value |= ctrl ? 0 : 0x0008;
355			phy_ioctl(fd, 1, i, 0x1e, &value);
356			value = 0x3300;
357			break;
358		case SWITCH_BCM53115:
359		case SWITCH_BCM53125:
360			/* fall through */
361#endif
362		case SWITCH_BCM5301x:
363			value = 0x1340;
364			value |= ctrl ? 0 : 0x0800;
365			break;
366		default:
367			continue;
368		}
369
370		/* issue write */
371		phy_ioctl(fd, 1, i, 0, &value);
372	}
373
374	close(fd);
375
376	return 0;
377}
378
379#define IMAGE_HEADER "HDR0"
380#define MAX_VERSION_LEN 64
381#define MAX_PID_LEN 12
382#define MAX_HW_COUNT 4
383
384/*
385 * 0: illegal image
386 * 1: legal image
387 */
388int
389check_crc(char *fname)
390{
391	FILE *fp;
392	int ret = 1;
393	int first_read = 1;
394	unsigned int len, count;
395
396	struct trx_header trx;
397	uint32 crc;
398	static uint32 buf[16*1024];
399
400	fp = fopen(fname, "r");
401	if (fp == NULL)
402	{
403		_dprintf("Open trx fail!!!\n");
404		return 0;
405	}
406
407	/* Read header */
408	ret = fread((unsigned char *) &trx, 1, sizeof(struct trx_header), fp);
409	if (ret != sizeof(struct trx_header)) {
410		ret = 0;
411		_dprintf("read header error!!!\n");
412		goto done;
413	}
414
415	/* Checksum over header */
416	crc = hndcrc32((uint8 *) &trx.flag_version,
417		       sizeof(struct trx_header) - OFFSETOF(struct trx_header, flag_version),
418		       CRC32_INIT_VALUE);
419
420	for (len = ltoh32(trx.len) - sizeof(struct trx_header); len; len -= count) {
421		if (first_read) {
422			count = MIN(len, sizeof(buf) - sizeof(struct trx_header));
423			first_read = 0;
424		} else
425			count = MIN(len, sizeof(buf));
426
427		/* Read data */
428		ret = fread((unsigned char *) &buf, 1, count, fp);
429		if (ret != count) {
430			ret = 0;
431			_dprintf("read error!\n");
432			goto done;
433		}
434
435		/* Checksum over data */
436		crc = hndcrc32((uint8 *) &buf, count, crc);
437	}
438	/* Verify checksum */
439	//_dprintf("checksum: %u ? %u\n", ltoh32(trx.crc32), crc);
440	if (ltoh32(trx.crc32) != crc) {
441		ret = 0;
442		goto done;
443	}
444
445done:
446	fclose(fp);
447
448	return ret;
449}
450
451/*
452 * 0: illegal image
453 * 1: legal image
454 */
455
456int check_imageheader(char *buf, long *filelen)
457{
458	long aligned;
459
460	if (strncmp(buf, IMAGE_HEADER, sizeof(IMAGE_HEADER) - 1) == 0)
461	{
462		memcpy(&aligned, buf + sizeof(IMAGE_HEADER) - 1, sizeof(aligned));
463		*filelen = aligned;
464#ifdef RTCONFIG_DSL_TCLINUX
465		*filelen+=0x790000;
466#endif
467		_dprintf("image len: %x\n", aligned);
468		return 1;
469	}
470	else return 0;
471}
472
473/*
474 * 0: illegal image
475 * 1: legal image
476 *
477 * check product id, crc ..
478 */
479
480int check_imagefile(char *fname)
481{
482	FILE *fp;
483	struct version_t {
484		uint8_t ver[4];			/* Firmware version */
485		uint8_t pid[MAX_PID_LEN];	/* Product Id */
486		uint8_t hw[MAX_HW_COUNT][4];	/* Compatible hw list lo maj.min, hi maj.min */
487#ifdef RTCONFIG_BCMWL6A
488		uint16_t sn;
489		uint16_t en;
490#endif
491		uint8_t	pad[0];			/* Padding up to MAX_VERSION_LEN */
492	} version;
493	int i, model;
494
495	fp = fopen(fname, "r");
496	if (fp == NULL)
497		return 0;
498
499	fseek(fp, -MAX_VERSION_LEN, SEEK_END);
500	fread(&version, 1, sizeof(version), fp);
501	fclose(fp);
502
503	_dprintf("productid field in image: %.12s\n", version.pid);
504
505	for (i = 0; i < sizeof(version); i++)
506		_dprintf("%02x ", ((uint8_t *)&version)[i]);
507	_dprintf("\n");
508
509	/* safe strip trailing spaces */
510	for (i = 0; i < MAX_PID_LEN && version.pid[i] != '\0'; i++);
511	for (i--; i >= 0 && version.pid[i] == '\x20'; i--)
512		version.pid[i] = '\0';
513
514	if (!check_crc(fname)) {
515		_dprintf("check crc error!!!\n");
516		return 0;
517	}
518
519#if defined(RTCONFIG_BCMWL6A) && !(defined(RTCONFIG_BCM7) || defined(RTCONFIG_BCM_7114))
520	doSystem("nvram set cpurev=`cat /dev/mtd0 | grep cpurev | cut -d \"=\" -f 2`");
521	if (nvram_match("cpurev", "c0") &&
522	   (!version.sn ||
523	    !version.en ||
524	     version.sn < 380 ||
525	    (version.sn == 380 && version.en < 738)))
526	{
527		dbg("version check fail!\n");
528		return 0;
529	}
530#endif
531
532	model = get_model();
533
534	/* compare up to the first \0 or MAX_PID_LEN
535	 * nvram productid or hw model's original productid */
536	if (strncmp(nvram_safe_get("productid"), (char *) version.pid, MAX_PID_LEN) == 0 ||
537	    strncmp(get_modelid(model), (char *) (char *) version.pid, MAX_PID_LEN) == 0)
538	{
539		return 1;
540	}
541
542	/* common RT-N12 productid FW image */
543	if ((model == MODEL_RTN12B1 || model == MODEL_RTN12C1 ||
544	     model == MODEL_RTN12D1 || model == MODEL_RTN12VP || model == MODEL_RTN12HP || model == MODEL_RTN12HP_B1 ||model == MODEL_APN12HP) &&
545	     strncmp(get_modelid(MODEL_RTN12), (char *) version.pid, MAX_PID_LEN) == 0)
546		return 1;
547
548	return 0;
549}
550
551#ifdef RTCONFIG_QTN
552char *wl_vifname_qtn(int unit, int subunit)
553{
554	static char tmp[128];
555
556	if ((subunit > 0) && (subunit < 4))
557	{
558		sprintf(tmp, "wifi%d", subunit);
559		return strdup(tmp);
560	}
561	else
562		return strdup("");
563}
564#endif
565
566int get_radio(int unit, int subunit)
567{
568	int n = 0;
569
570	//_dprintf("get radio %x %x %s\n", unit, subunit, nvram_safe_get(wl_nvname("ifname", unit, subunit)));
571
572#ifdef RTCONFIG_QTN
573	int ret;
574	char interface_status = 0;
575
576	if (unit)
577	{
578		if(!rpc_qtn_ready())
579			return -1;
580
581		if (subunit > 0)
582		{
583			ret = qcsapi_interface_get_status(wl_vifname_qtn(unit, subunit), &interface_status);
584//			if (ret < 0)
585//				dbG("Qcsapi qcsapi_interface_get_status %s error, return: %d\n", wl_vifname_qtn(unit, subunit), ret);
586
587			return interface_status;
588		}
589		else
590		{
591			ret = qcsapi_wifi_rfstatus((qcsapi_unsigned_int *) &n);
592//			if (ret < 0)
593//				dbG("Qcsapi qcsapi_wifi_rfstatus %s error, return: %d\n", wl_vifname_qtn(unit, subunit), ret);
594
595			return n;
596		}
597	}
598	else
599#endif
600
601	return (wl_ioctl(nvram_safe_get(wl_nvname("ifname", unit, subunit)), WLC_GET_RADIO, &n, sizeof(n)) == 0) &&
602		!(n & (WL_RADIO_SW_DISABLE | WL_RADIO_HW_DISABLE));
603}
604
605void set_radio(int on, int unit, int subunit)
606{
607	uint32 n;
608	char tmp[100], prefix[] = "wlXXXXXXXXXXXXXX";
609
610#ifdef RTCONFIG_QTN
611	if (unit) {
612		if (!rpc_qtn_ready())
613			return;
614
615		rpc_set_radio(unit, subunit, on);
616
617		return;
618	}
619#endif
620	//_dprintf("set radio %x %x %x %s\n", on, unit, subunit, nvram_safe_get(wl_nvname("ifname", unit, subunit)));
621
622	if (subunit > 0)
623		snprintf(prefix, sizeof(prefix), "wl%d.%d_", unit, subunit);
624	else
625		snprintf(prefix, sizeof(prefix), "wl%d_", unit);
626
627	//if (nvram_match(strcat_r(prefix, "radio", tmp), "0")) return;
628
629#if defined(RTAC66U) || defined(BCM4352)
630	if ((unit == 1) & (subunit < 1)) {
631		if (on) {
632#ifndef RTCONFIG_LED_BTN
633			if (!(nvram_get_int("sw_mode")==SW_MODE_AP && nvram_get_int("wlc_psta")==1 && nvram_get_int("wlc_band")==0)) {
634				nvram_set("led_5g", "1");
635				led_control(LED_5G, LED_ON);
636			}
637#else
638			nvram_set("led_5g", "1");
639			if (nvram_get_int("AllLED"))
640				led_control(LED_5G, LED_ON);
641#endif
642		}
643		else {
644			nvram_set("led_5g", "0");
645			led_control(LED_5G, LED_OFF);
646		}
647	}
648#endif
649
650	if (subunit > 0) {
651		sprintf(tmp, "%d", subunit);
652		if (on) eval("wl", "-i", nvram_safe_get(wl_nvname("ifname", unit, 0)), "bss", "-C", tmp, "up");
653		else eval("wl", "-i", nvram_safe_get(wl_nvname("ifname", unit, 0)), "bss", "-C", tmp, "down");
654
655		return;
656	}
657
658#ifndef WL_BSS_INFO_VERSION
659#error WL_BSS_INFO_VERSION
660#endif
661
662#if WL_BSS_INFO_VERSION >= 108
663	n = on ? (WL_RADIO_SW_DISABLE << 16) : ((WL_RADIO_SW_DISABLE << 16) | 1);
664	wl_ioctl(nvram_safe_get(wl_nvname("ifname", unit, subunit)), WLC_SET_RADIO, &n, sizeof(n));
665	if (!on) {
666		//led(LED_WLAN, 0);
667		//led(LED_DIAG, 0);
668	}
669#else
670	n = on ? 0 : WL_RADIO_SW_DISABLE;
671	wl_ioctl(nvram_safe_get(wl_nvname("ifname", unit, subunit)), WLC_SET_RADIO, &n, sizeof(n));
672	if (!on) {
673		//led(LED_DIAG, 0);
674	}
675#endif
676}
677