Root/package/broadcom-diag/src/diag.c

1/*
2 * diag.c - GPIO interface driver for Broadcom boards
3 *
4 * Copyright (C) 2006 Mike Baker <mbm@openwrt.org>,
5 * Copyright (C) 2006-2007 Felix Fietkau <nbd@openwrt.org>
6 * Copyright (C) 2008 Andy Boyett <agb@openwrt.org>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 *
22 */
23#include <linux/module.h>
24#include <linux/pci.h>
25#include <linux/kmod.h>
26#include <linux/proc_fs.h>
27#include <linux/timer.h>
28#include <linux/version.h>
29#include <asm/uaccess.h>
30#include <linux/workqueue.h>
31#include <linux/skbuff.h>
32#include <linux/netlink.h>
33#include <linux/kobject.h>
34#include <net/sock.h>
35extern u64 uevent_next_seqnum(void);
36
37#include "gpio.h"
38#include "diag.h"
39#define getvar(str) (nvram_get(str)?:"")
40
41static inline int startswith (char *source, char *cmp) { return !strncmp(source,cmp,strlen(cmp)); }
42static int fill_event(struct event_t *);
43static unsigned int gpiomask = 0;
44module_param(gpiomask, int, 0644);
45
46enum {
47    /* Linksys */
48    WAP54GV1,
49    WAP54GV2,
50    WAP54GV3,
51    WRT54GV1,
52    WRT54G,
53    WRTSL54GS,
54    WRT54G3G,
55    WRT54G3GV2_VF,
56    WRT150NV1,
57    WRT150NV11,
58    WRT160NV1,
59    WRT160NV3,
60    WRT300NV11,
61    WRT350N,
62    WRT600N,
63    WRT600NV11,
64    WRT610N,
65    WRT610NV2,
66    E3000V1,
67
68    /* ASUS */
69    WLHDD,
70    WL300G,
71    WL320GE,
72    WL330GE,
73    WL500G,
74    WL500GD,
75    WL500GP,
76    WL500GPV2,
77    WL500W,
78    WL520GC,
79    WL520GU,
80    ASUS_4702,
81    WL700GE,
82    RTN16,
83
84    /* Buffalo */
85    WBR2_G54,
86    WHR_G54S,
87    WHR_HP_G54,
88    WHR_G125,
89    WHR2_A54G54,
90    WLA2_G54L,
91    WZR_G300N,
92    WZR_RS_G54,
93    WZR_RS_G54HP,
94    BUFFALO_UNKNOWN,
95    BUFFALO_UNKNOWN_4710,
96
97    /* Siemens */
98    SE505V1,
99    SE505V2,
100
101    /* US Robotics */
102    USR5461,
103
104    /* Dell */
105    TM2300,
106    TM2300V2,
107
108    /* Motorola */
109    WE800G,
110    WR850GV1,
111    WR850GV2V3,
112    WR850GP,
113
114    /* Belkin */
115    BELKIN_UNKNOWN,
116    BELKIN_F7D4301,
117
118    /* Netgear */
119    WGT634U,
120    WNR834BV1,
121    WNR834BV2,
122
123    /* Trendware */
124    TEW411BRPP,
125
126    /* SimpleTech */
127    STI_NAS,
128
129    /* D-Link */
130    DIR130,
131    DIR320,
132    DIR330,
133    DWL3150,
134
135    /* Sitecom */
136    WL105B,
137
138    /* Western Digital */
139    WDNetCenter,
140
141    /* Askey */
142    RT210W,
143
144    /* OvisLink */
145    WL1600GL,
146
147    /* Microsoft */
148    MN700,
149
150    /* Edimax */
151    PS1208MFG,
152};
153
154static void __init bcm4780_init(void) {
155        int pin = 1 << 3;
156
157        /* Enables GPIO 3 that controls HDD and led power on ASUS WL-700gE */
158        printk(MODULE_NAME ": Spinning up HDD and enabling leds\n");
159        bcm47xx_gpio_outen(pin, pin);
160        bcm47xx_gpio_control(pin, 0);
161        bcm47xx_gpio_out(pin, pin);
162
163        /* Wait 5s, so the HDD can spin up */
164        set_current_state(TASK_INTERRUPTIBLE);
165        schedule_timeout(HZ * 5);
166}
167
168static void __init NetCenter_init(void) {
169        /* unset pin 6 (+12V) */
170        int pin = 1 << 6;
171        bcm47xx_gpio_outen(pin, pin);
172        bcm47xx_gpio_control(pin, 0);
173        bcm47xx_gpio_out(pin, pin);
174        /* unset pin 1 (turn off red led, blue will light alone if +5V comes up) */
175        pin = 1 << 1;
176        bcm47xx_gpio_outen(pin, pin);
177        bcm47xx_gpio_control(pin, 0);
178        bcm47xx_gpio_out(pin, pin);
179        /* unset pin 3 (+5V) and wait 5 seconds (harddisk spin up) */
180        bcm4780_init();
181}
182
183static void __init bcm57xx_init(void) {
184    int pin = 1 << 2;
185
186    /* FIXME: switch comes up, but port mappings/vlans not right */
187    bcm47xx_gpio_outen(pin, pin);
188    bcm47xx_gpio_control(pin, 0);
189    bcm47xx_gpio_out(pin, pin);
190}
191
192static struct platform_t __initdata platforms[] = {
193    /* Linksys */
194    [WAP54GV1] = {
195        .name = "Linksys WAP54G V1",
196        .buttons = {
197            { .name = "reset", .gpio = 1 << 0 },
198        },
199        .leds = {
200            { .name = "diag", .gpio = 1 << 3 },
201            { .name = "wlan", .gpio = 1 << 4 },
202        },
203    },
204    [WAP54GV2] = {
205        .name = "Linksys WAP54G V2",
206        .buttons = {
207            { .name = "reset", .gpio = 1 << 0 },
208        },
209        .leds = {
210            { .name = "wlan", .gpio = 1 << 5, .polarity = REVERSE },
211            /* GPIO 6 is b44 (eth0, LAN) PHY power */
212        },
213    },
214    [WAP54GV3] = {
215        .name = "Linksys WAP54G V3",
216        .buttons = {
217            /* FIXME: verify this */
218            { .name = "reset", .gpio = 1 << 7 },
219            { .name = "ses", .gpio = 1 << 0 },
220        },
221        .leds = {
222            /* FIXME: diag? */
223            { .name = "ses", .gpio = 1 << 1 },
224        },
225    },
226    [WRT54GV1] = {
227        .name = "Linksys WRT54G V1.x",
228        .buttons = {
229            { .name = "reset", .gpio = 1 << 6 },
230        },
231        .leds = {
232            { .name = "diag", .gpio = 0x13 | GPIO_TYPE_EXTIF, .polarity = NORMAL },
233            { .name = "dmz", .gpio = 0x12 | GPIO_TYPE_EXTIF, .polarity = NORMAL },
234        },
235    },
236    [WRT54G] = {
237        .name = "Linksys WRT54G/GS/GL",
238        .buttons = {
239            { .name = "reset", .gpio = 1 << 6 },
240            { .name = "ses", .gpio = 1 << 4 },
241        },
242        .leds = {
243            { .name = "power", .gpio = 1 << 1, .polarity = NORMAL },
244            { .name = "dmz", .gpio = 1 << 7, .polarity = REVERSE },
245            { .name = "ses_white", .gpio = 1 << 2, .polarity = REVERSE },
246            { .name = "ses_orange", .gpio = 1 << 3, .polarity = REVERSE },
247            { .name = "wlan", .gpio = 1 << 0, .polarity = REVERSE },
248        },
249    },
250    [WRTSL54GS] = {
251        .name = "Linksys WRTSL54GS",
252        .buttons = {
253            { .name = "reset", .gpio = 1 << 6 },
254            { .name = "ses", .gpio = 1 << 4 },
255        },
256        .leds = {
257            { .name = "power", .gpio = 1 << 1, .polarity = NORMAL },
258            { .name = "dmz", .gpio = 1 << 0, .polarity = REVERSE },
259            { .name = "ses_white", .gpio = 1 << 5, .polarity = REVERSE },
260            { .name = "ses_orange", .gpio = 1 << 7, .polarity = REVERSE },
261        },
262    },
263    [WRT54G3G] = {
264        .name = "Linksys WRT54G3G",
265        .buttons = {
266            { .name = "reset", .gpio = 1 << 6 },
267            { .name = "3g", .gpio = 1 << 4 },
268        },
269        .leds = {
270            { .name = "power", .gpio = 1 << 1, .polarity = NORMAL },
271            { .name = "dmz", .gpio = 1 << 7, .polarity = REVERSE },
272            { .name = "3g_green", .gpio = 1 << 2, .polarity = NORMAL },
273            { .name = "3g_blue", .gpio = 1 << 3, .polarity = NORMAL },
274            { .name = "3g_blink", .gpio = 1 << 5, .polarity = NORMAL },
275        },
276    },
277    [WRT54G3GV2_VF] = {
278        .name = "Linksys WRT54G3GV2-VF",
279        .buttons = {
280            { .name = "reset", .gpio = 1 << 6 },
281            { .name = "3g", .gpio = 1 << 5 },
282        },
283        .leds = {
284            { .name = "power", .gpio = 1 << 1, .polarity = NORMAL },
285            { .name = "3g_green", .gpio = 1 << 2, .polarity = NORMAL },
286            { .name = "3g_blue", .gpio = 1 << 3, .polarity = NORMAL },
287        },
288    },
289    [WRT150NV1] = {
290        .name = "Linksys WRT150N V1",
291        .buttons = {
292            { .name = "reset", .gpio = 1 << 6 },
293            { .name = "ses", .gpio = 1 << 4 },
294        },
295        .leds = {
296            { .name = "power", .gpio = 1 << 1, .polarity = NORMAL },
297            { .name = "ses_green", .gpio = 1 << 5, .polarity = REVERSE },
298            { .name = "ses_amber", .gpio = 1 << 3, .polarity = REVERSE },
299        },
300    },
301    [WRT150NV11] = {
302        .name = "Linksys WRT150N V1.1",
303        .buttons = {
304            { .name = "reset", .gpio = 1 << 6 },
305            { .name = "ses", .gpio = 1 << 4 },
306        },
307        .leds = {
308            { .name = "power", .gpio = 1 << 1, .polarity = NORMAL },
309            { .name = "ses_green", .gpio = 1 << 5, .polarity = REVERSE },
310            { .name = "ses_amber", .gpio = 1 << 3, .polarity = REVERSE },
311        },
312    },
313    [WRT160NV1] = {
314        .name = "Linksys WRT160N v1.x",
315        .buttons = {
316            { .name = "reset", .gpio = 1 << 6 },
317            { .name = "ses", .gpio = 1 << 4 },
318        },
319        .leds = {
320            { .name = "power", .gpio = 1 << 1, .polarity = NORMAL },
321            { .name = "ses_blue", .gpio = 1 << 5, .polarity = REVERSE },
322            { .name = "ses_orange", .gpio = 1 << 3, .polarity = REVERSE },
323        },
324    },
325    [WRT160NV3] = {
326        .name = "Linksys WRT160N V3",
327        .buttons = {
328            { .name = "reset", .gpio = 1 << 6 },
329            { .name = "ses", .gpio = 1 << 5 },
330        },
331        .leds = {
332            { .name = "power", .gpio = 1 << 1, .polarity = NORMAL },
333            { .name = "ses_blue", .gpio = 1 << 4, .polarity = REVERSE },
334            { .name = "ses_orange", .gpio = 1 << 2, .polarity = REVERSE },
335        },
336    },
337    [WRT300NV11] = {
338        .name = "Linksys WRT300N V1.1",
339        .buttons = {
340            { .name = "reset", .gpio = 1 << 6 }, // "Reset" on back panel
341            { .name = "ses", .gpio = 1 << 4 }, // "Reserved" on top panel
342        },
343        .leds = {
344            { .name = "power", .gpio = 1 << 1, .polarity = NORMAL }, // "Power"
345            { .name = "ses_amber", .gpio = 1 << 3, .polarity = REVERSE }, // "Security" Amber
346            { .name = "ses_green", .gpio = 1 << 5, .polarity = REVERSE }, // "Security" Green
347        },
348        .platform_init = bcm57xx_init,
349    },
350    [WRT350N] = {
351        .name = "Linksys WRT350N",
352        .buttons = {
353            { .name = "reset", .gpio = 1 << 6 },
354            { .name = "ses", .gpio = 1 << 8 },
355        },
356        .leds = {
357            { .name = "power", .gpio = 1 << 1, .polarity = NORMAL },
358            { .name = "ses_amber", .gpio = 1 << 3, .polarity = REVERSE },
359            { .name = "ses_green", .gpio = 1 << 9, .polarity = REVERSE },
360            { .name = "usb_blink", .gpio = 1 << 10, .polarity = REVERSE },
361            { .name = "usb", .gpio = 1 << 11, .polarity = REVERSE },
362        },
363        .platform_init = bcm57xx_init,
364    },
365    [WRT600N] = {
366        .name = "Linksys WRT600N",
367        .buttons = {
368            { .name = "reset", .gpio = 1 << 6 },
369            { .name = "ses", .gpio = 1 << 7 },
370        },
371        .leds = {
372            { .name = "power", .gpio = 1 << 2, .polarity = REVERSE }, // Power LED
373            { .name = "usb", .gpio = 1 << 3, .polarity = REVERSE }, // USB LED
374            { .name = "wl0_ses_amber", .gpio = 1 << 8, .polarity = REVERSE }, // 2.4Ghz LED Amber
375            { .name = "wl0_ses_green", .gpio = 1 << 9, .polarity = REVERSE }, // 2.4Ghz LED Green
376            { .name = "wl1_ses_amber", .gpio = 1 << 10, .polarity = REVERSE }, // 5.6Ghz LED Amber
377            { .name = "wl1_ses_green", .gpio = 1 << 11, .polarity = REVERSE }, // 5.6Ghz LED Green
378        },
379        .platform_init = bcm57xx_init,
380    },
381    [WRT600NV11] = {
382        .name = "Linksys WRT600N V1.1",
383        .buttons = {
384            { .name = "reset", .gpio = 1 << 6 },
385            { .name = "ses", .gpio = 1 << 7 },
386        },
387        .leds = {
388            { .name = "power", .gpio = 1 << 2, .polarity = REVERSE }, // Power LED
389            { .name = "usb", .gpio = 1 << 3, .polarity = REVERSE }, // USB LED
390            { .name = "wl0_ses_amber", .gpio = 1 << 8, .polarity = REVERSE }, // 2.4Ghz LED Amber
391            { .name = "wl0_ses_green", .gpio = 1 << 9, .polarity = REVERSE }, // 2.4Ghz LED Green
392            { .name = "wl1_ses_amber", .gpio = 1 << 10, .polarity = REVERSE }, // 5.6Ghz LED Amber
393            { .name = "wl1_ses_green", .gpio = 1 << 11, .polarity = REVERSE }, // 5.6Ghz LED Green
394        },
395        .platform_init = bcm57xx_init,
396    },
397    [WRT610N] = {
398        .name = "Linksys WRT610N",
399        .buttons = {
400            { .name = "reset", .gpio = 1 << 6 },
401            { .name = "ses", .gpio = 1 << 8 },
402        },
403        .leds = {
404            { .name = "power", .gpio = 1 << 1, .polarity = NORMAL }, // Power LED
405            { .name = "usb", .gpio = 1 << 0, .polarity = REVERSE }, // USB LED
406            { .name = "ses_amber", .gpio = 1 << 3, .polarity = REVERSE }, // WiFi protected setup LED amber
407            { .name = "ses_blue", .gpio = 1 << 9, .polarity = REVERSE }, // WiFi protected setup LED blue
408        },
409    },
410    [WRT610NV2] = {
411        .name = "Linksys WRT610N V2",
412        .buttons = {
413            { .name = "reset", .gpio = 1 << 6 },
414            { .name = "ses", .gpio = 1 << 4 },
415        },
416        .leds = {
417            { .name = "power", .gpio = 1 << 5, .polarity = NORMAL }, // Power LED
418            { .name = "usb", .gpio = 1 << 7, .polarity = NORMAL }, // USB LED
419            { .name = "ses_amber", .gpio = 1 << 0, .polarity = REVERSE }, // WiFi protected setup LED amber
420            { .name = "ses_blue", .gpio = 1 << 3, .polarity = REVERSE }, // WiFi protected setup LED blue
421            { .name = "wlan", .gpio = 1 << 1, .polarity = NORMAL }, // Wireless LED
422        },
423    },
424    [E3000V1] = {
425        .name = "Linksys E3000 V1",
426        .buttons = {
427            { .name = "reset", .gpio = 1 << 6 },
428            { .name = "ses", .gpio = 1 << 4 },
429        },
430        .leds = {
431            { .name = "power", .gpio = 1 << 5, .polarity = NORMAL }, // Power LED
432            { .name = "usb", .gpio = 1 << 7, .polarity = NORMAL }, // USB LED
433            { .name = "ses_amber", .gpio = 1 << 0, .polarity = REVERSE }, // WiFi protected setup LED amber
434            { .name = "ses_blue", .gpio = 1 << 3, .polarity = REVERSE }, // WiFi protected setup LED blue
435            { .name = "wlan", .gpio = 1 << 1, .polarity = NORMAL }, // Wireless LED
436        },
437    },
438    /* Asus */
439    [WLHDD] = {
440        .name = "ASUS WL-HDD",
441        .buttons = {
442            { .name = "reset", .gpio = 1 << 6 },
443        },
444        .leds = {
445            { .name = "power", .gpio = 1 << 0, .polarity = REVERSE },
446            { .name = "usb", .gpio = 1 << 2, .polarity = REVERSE },
447        },
448    },
449    [WL300G] = {
450        .name = "ASUS WL-300g",
451        .buttons = {
452            { .name = "reset", .gpio = 1 << 6 },
453        },
454        .leds = {
455            { .name = "power", .gpio = 1 << 0, .polarity = REVERSE },
456        },
457    },
458    [WL320GE] = {
459        .name = "ASUS WL-320gE/WL-320gP",
460        .buttons = {
461            { .name = "reset", .gpio = 1 << 6 },
462        },
463        .leds = {
464            { .name = "wlan", .gpio = 1 << 0, .polarity = REVERSE },
465            { .name = "power", .gpio = 1 << 2, .polarity = REVERSE },
466            { .name = "link", .gpio = 1 << 11, .polarity = REVERSE },
467        },
468    },
469    [WL330GE] = {
470        .name = "ASUS WL-330gE",
471        .buttons = {
472            { .name = "reset", .gpio = 1 << 2 },
473        },
474        .leds = {
475            { .name = "power", .gpio = 1 << 0, .polarity = REVERSE },
476        },
477    },
478    [WL500G] = {
479        .name = "ASUS WL-500g",
480        .buttons = {
481            { .name = "reset", .gpio = 1 << 6 },
482        },
483        .leds = {
484            { .name = "power", .gpio = 1 << 0, .polarity = REVERSE },
485        },
486    },
487    [WL500GD] = {
488        .name = "ASUS WL-500g Deluxe",
489        .buttons = {
490            { .name = "reset", .gpio = 1 << 6 },
491        },
492        .leds = {
493            { .name = "power", .gpio = 1 << 0, .polarity = REVERSE },
494        },
495    },
496    [WL500GP] = {
497        .name = "ASUS WL-500g Premium",
498        .buttons = {
499            { .name = "reset", .gpio = 1 << 0 },
500            { .name = "ses", .gpio = 1 << 4 },
501        },
502        .leds = {
503            { .name = "power", .gpio = 1 << 1, .polarity = REVERSE },
504        },
505    },
506    [WL500GPV2] = {
507        .name = "ASUS WL-500g Premium V2",
508        .buttons = {
509            { .name = "reset", .gpio = 1 << 2 },
510            { .name = "ses", .gpio = 1 << 3 },
511        },
512        .leds = {
513            { .name = "power", .gpio = 1 << 0, .polarity = REVERSE },
514            { .name = "wlan", .gpio = 1 << 1, .polarity = REVERSE },
515        },
516    },
517    [WL500W] = {
518        .name = "ASUS WL-500W",
519        .buttons = {
520            { .name = "reset", .gpio = 1 << 6 },
521            { .name = "ses", .gpio = 1 << 7 },
522        },
523        .leds = {
524            { .name = "power", .gpio = 1 << 5, .polarity = REVERSE },
525        },
526    },
527    [WL520GC] = {
528        .name = "ASUS WL-520GC",
529        .buttons = {
530            { .name = "reset", .gpio = 1 << 2 },
531            { .name = "ses", .gpio = 1 << 3 },
532        },
533        .leds = {
534        { .name = "power", .gpio = 1 << 0, .polarity = REVERSE },
535            { .name = "wlan", .gpio = 1 << 1, .polarity = REVERSE },
536        },
537    },
538    [WL520GU] = {
539        .name = "ASUS WL-520gU",
540        .buttons = {
541            { .name = "reset", .gpio = 1 << 2 },
542            { .name = "ses", .gpio = 1 << 3 },
543        },
544        .leds = {
545            { .name = "power", .gpio = 1 << 0, .polarity = REVERSE },
546            { .name = "wlan", .gpio = 1 << 1, .polarity = REVERSE },
547        },
548    },
549    [ASUS_4702] = {
550        .name = "ASUS (unknown, BCM4702)",
551        .buttons = {
552            { .name = "reset", .gpio = 1 << 6 },
553        },
554        .leds = {
555            { .name = "power", .gpio = 1 << 0, .polarity = REVERSE },
556        },
557    },
558    [WL700GE] = {
559        .name = "ASUS WL-700gE",
560        .buttons = {
561            { .name = "reset", .gpio = 1 << 7 }, // on back, hardwired, always resets device regardless OS state
562            { .name = "ses", .gpio = 1 << 4 }, // on back, actual name ezsetup
563            { .name = "power", .gpio = 1 << 0 }, // on front
564            { .name = "copy", .gpio = 1 << 6 }, // on front
565        },
566        .leds = {
567#if 0
568            // GPIO that controls power led also enables/disables some essential functions
569            // - power to HDD
570            // - switch leds
571            { .name = "power", .gpio = 1 << 3, .polarity = NORMAL }, // actual name power
572#endif
573            { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE }, // actual name ready
574        },
575        .platform_init = bcm4780_init,
576    },
577    [RTN16] = {
578        .name = "ASUS RT-N16",
579        .buttons = {
580            { .name = "reset", .gpio = 1 << 8 },
581            { .name = "ses", .gpio = 1 << 5 },
582        },
583        .leds = {
584            { .name = "power", .gpio = 1 << 1, .polarity = REVERSE },
585            { .name = "wlan", .gpio = 1 << 7, .polarity = NORMAL },
586        },
587    },
588    /* Buffalo */
589    [WHR_G54S] = {
590        .name = "Buffalo WHR-G54S",
591        .buttons = {
592            { .name = "reset", .gpio = 1 << 4 },
593            { .name = "bridge", .gpio = 1 << 5 },
594            { .name = "ses", .gpio = 1 << 0 },
595        },
596        .leds = {
597            { .name = "diag", .gpio = 1 << 7, .polarity = REVERSE },
598            { .name = "internal", .gpio = 1 << 3, .polarity = REVERSE },
599            { .name = "ses", .gpio = 1 << 6, .polarity = REVERSE },
600            { .name = "bridge", .gpio = 1 << 1, .polarity = REVERSE },
601            { .name = "wlan", .gpio = 1 << 2, .polarity = REVERSE },
602        },
603    },
604    [WBR2_G54] = {
605        .name = "Buffalo WBR2-G54",
606        /* FIXME: verify */
607        .buttons = {
608            { .name = "reset", .gpio = 1 << 7 },
609        },
610        .leds = {
611            { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE },
612        },
613    },
614    [WHR_HP_G54] = {
615        .name = "Buffalo WHR-HP-G54",
616        .buttons = {
617            { .name = "reset", .gpio = 1 << 4 },
618            { .name = "bridge", .gpio = 1 << 5 },
619            { .name = "ses", .gpio = 1 << 0 },
620        },
621        .leds = {
622            { .name = "diag", .gpio = 1 << 7, .polarity = REVERSE },
623            { .name = "internal", .gpio = 1 << 3, .polarity = REVERSE },
624            { .name = "bridge", .gpio = 1 << 1, .polarity = REVERSE },
625            { .name = "ses", .gpio = 1 << 6, .polarity = REVERSE },
626            { .name = "wlan", .gpio = 1 << 2, .polarity = REVERSE },
627        },
628    },
629    [WHR_G125] = {
630        .name = "Buffalo WHR-G125",
631        .buttons = {
632            { .name = "reset", .gpio = 1 << 4 },
633            { .name = "bridge", .gpio = 1 << 5 },
634            { .name = "ses", .gpio = 1 << 0 },
635        },
636        .leds = {
637            { .name = "diag", .gpio = 1 << 7, .polarity = REVERSE },
638            { .name = "internal", .gpio = 1 << 3, .polarity = REVERSE },
639            { .name = "bridge", .gpio = 1 << 1, .polarity = REVERSE },
640            { .name = "ses", .gpio = 1 << 6, .polarity = REVERSE },
641            { .name = "wlan", .gpio = 1 << 2, .polarity = REVERSE },
642        },
643    },
644    [WHR2_A54G54] = {
645        .name = "Buffalo WHR2-A54G54",
646        .buttons = {
647            { .name = "reset", .gpio = 1 << 4 },
648        },
649        .leds = {
650            { .name = "diag", .gpio = 1 << 7, .polarity = REVERSE },
651        },
652    },
653    [WLA2_G54L] = {
654        .name = "Buffalo WLA2-G54L",
655        /* FIXME: verify */
656        .buttons = {
657            { .name = "reset", .gpio = 1 << 7 },
658        },
659        .leds = {
660            { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE },
661        },
662    },
663    [WZR_G300N] = {
664        .name = "Buffalo WZR-G300N",
665        .buttons = {
666            { .name = "reset", .gpio = 1 << 4 },
667        },
668        .leds = {
669            { .name = "diag", .gpio = 1 << 7, .polarity = REVERSE },
670            { .name = "bridge", .gpio = 1 << 1, .polarity = REVERSE },
671            { .name = "ses", .gpio = 1 << 6, .polarity = REVERSE },
672        },
673    },
674    [WZR_RS_G54] = {
675        .name = "Buffalo WZR-RS-G54",
676        .buttons = {
677            { .name = "ses", .gpio = 1 << 0 },
678            { .name = "reset", .gpio = 1 << 4 },
679        },
680        .leds = {
681            { .name = "diag", .gpio = 1 << 7, .polarity = REVERSE },
682            { .name = "ses", .gpio = 1 << 6, .polarity = REVERSE },
683            { .name = "vpn", .gpio = 1 << 1, .polarity = REVERSE },
684        },
685    },
686    [WZR_RS_G54HP] = {
687        .name = "Buffalo WZR-RS-G54HP",
688        .buttons = {
689            { .name = "ses", .gpio = 1 << 0 },
690            { .name = "reset", .gpio = 1 << 4 },
691        },
692        .leds = {
693            { .name = "diag", .gpio = 1 << 7, .polarity = REVERSE },
694            { .name = "ses", .gpio = 1 << 6, .polarity = REVERSE },
695            { .name = "vpn", .gpio = 1 << 1, .polarity = REVERSE },
696        },
697    },
698    [BUFFALO_UNKNOWN] = {
699        .name = "Buffalo (unknown)",
700        .buttons = {
701            { .name = "reset", .gpio = 1 << 7 },
702        },
703        .leds = {
704            { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE },
705        },
706    },
707    [BUFFALO_UNKNOWN_4710] = {
708        .name = "Buffalo (unknown, BCM4710)",
709        .buttons = {
710            { .name = "reset", .gpio = 1 << 4 },
711        },
712        .leds = {
713            { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE },
714        },
715    },
716    /* Siemens */
717    [SE505V1] = {
718        .name = "Siemens SE505 V1",
719        .buttons = {
720            /* No usable buttons */
721        },
722        .leds = {
723// { .name = "power", .gpio = 1 << 0 .polarity = REVERSE }, // Usable when retrofitting D26 (?)
724            { .name = "dmz", .gpio = 1 << 4, .polarity = REVERSE }, // actual name WWW
725            { .name = "wlan", .gpio = 1 << 3, .polarity = REVERSE },
726        },
727    },
728    [SE505V2] = {
729        .name = "Siemens SE505 V2",
730        .buttons = {
731            /* No usable buttons */
732        },
733        .leds = {
734            { .name = "power", .gpio = 1 << 5, .polarity = REVERSE },
735            { .name = "dmz", .gpio = 1 << 0, .polarity = REVERSE }, // actual name WWW
736            { .name = "wlan", .gpio = 1 << 3, .polarity = REVERSE },
737        },
738    },
739    /* US Robotics */
740    [USR5461] = {
741        .name = "U.S. Robotics USR5461",
742        .buttons = {
743            /* No usable buttons */
744        },
745        .leds = {
746            { .name = "wlan", .gpio = 1 << 0, .polarity = REVERSE },
747            { .name = "printer", .gpio = 1 << 1, .polarity = REVERSE },
748        },
749    },
750    /* Dell */
751    [TM2300] = {
752        .name = "Dell TrueMobile 2300",
753        .buttons = {
754            { .name = "reset", .gpio = 1 << 0 },
755        },
756        .leds = {
757            { .name = "wlan", .gpio = 1 << 6, .polarity = REVERSE },
758            { .name = "power", .gpio = 1 << 7, .polarity = REVERSE },
759        },
760    },
761    [TM2300V2] = {
762        .name = "Dell TrueMobile 2300 v2",
763        .buttons = {
764            { .name = "reset", .gpio = 1 << 0 },
765        },
766        .leds = {
767            { .name = "wlan", .gpio = 1 << 6, .polarity = REVERSE },
768            { .name = "power", .gpio = 1 << 7, .polarity = REVERSE },
769        },
770    },
771    /* Motorola */
772    [WE800G] = {
773        .name = "Motorola WE800G",
774        .buttons = {
775            { .name = "reset", .gpio = 1 << 0 },
776        },
777        .leds = {
778            { .name = "power", .gpio = 1 << 4, .polarity = NORMAL },
779            { .name = "diag", .gpio = 1 << 2, .polarity = REVERSE },
780            { .name = "wlan_amber", .gpio = 1 << 1, .polarity = NORMAL },
781        },
782    },
783    [WR850GV1] = {
784        .name = "Motorola WR850G V1",
785        .buttons = {
786            { .name = "reset", .gpio = 1 << 0 },
787        },
788        .leds = {
789            { .name = "power", .gpio = 1 << 4, .polarity = NORMAL },
790            { .name = "diag", .gpio = 1 << 3, .polarity = REVERSE },
791            { .name = "dmz", .gpio = 1 << 6, .polarity = NORMAL },
792            { .name = "wlan_red", .gpio = 1 << 5, .polarity = REVERSE },
793            { .name = "wlan_green", .gpio = 1 << 7, .polarity = REVERSE },
794        },
795    },
796    [WR850GV2V3] = {
797        .name = "Motorola WR850G V2/V3",
798        .buttons = {
799            { .name = "reset", .gpio = 1 << 5 },
800        },
801        .leds = {
802            { .name = "power", .gpio = 1 << 1, .polarity = NORMAL },
803            { .name = "wlan", .gpio = 1 << 0, .polarity = REVERSE },
804            { .name = "wan", .gpio = 1 << 6, .polarity = INPUT },
805            { .name = "diag", .gpio = 1 << 7, .polarity = REVERSE },
806        },
807    },
808    [WR850GP] = {
809        .name = "Motorola WR850GP",
810        .buttons = {
811            { .name = "reset", .gpio = 1 << 5 },
812        },
813        .leds = {
814            { .name = "power", .gpio = 1 << 1, .polarity = NORMAL },
815            { .name = "wlan", .gpio = 1 << 0, .polarity = REVERSE },
816            { .name = "dmz", .gpio = 1 << 6, .polarity = REVERSE },
817            { .name = "diag", .gpio = 1 << 7, .polarity = REVERSE },
818        },
819    },
820
821    /* Belkin */
822    [BELKIN_UNKNOWN] = {
823        .name = "Belkin (unknown)",
824        /* FIXME: verify & add detection */
825        .buttons = {
826            { .name = "reset", .gpio = 1 << 7 },
827        },
828        .leds = {
829            { .name = "power", .gpio = 1 << 5, .polarity = NORMAL },
830            { .name = "wlan", .gpio = 1 << 3, .polarity = NORMAL },
831            { .name = "connected", .gpio = 1 << 0, .polarity = NORMAL },
832        },
833    },
834    [BELKIN_F7D4301] = {
835        .name = "Belkin PlayMax F7D4301",
836        .buttons = {
837            { .name = "reset", .gpio = 1 << 6 },
838            { .name = "wps", .gpio = 1 << 8 },
839        },
840        .leds = {
841            { .name = "power", .gpio = 1 << 11, .polarity = REVERSE },
842            { .name = "wlan", .gpio = 1 << 13, .polarity = REVERSE },
843            { .name = "led0", .gpio = 1 << 14, .polarity = REVERSE },
844            { .name = "led1", .gpio = 1 << 15, .polarity = REVERSE },
845        },
846    },
847    /* Netgear */
848    [WGT634U] = {
849        .name = "Netgear WGT634U",
850        .buttons = {
851            { .name = "reset", .gpio = 1 << 2 },
852        },
853        .leds = {
854            { .name = "power", .gpio = 1 << 3, .polarity = NORMAL },
855        },
856    },
857    /* Netgear */
858    [WNR834BV1] = {
859        .name = "Netgear WNR834B V1",
860        .buttons = { /* TODO: add reset button and confirm LEDs - GPIO from dd-wrt */ },
861        .leds = {
862            { .name = "power", .gpio = 1 << 4, .polarity = REVERSE },
863            { .name = "diag", .gpio = 1 << 5, .polarity = REVERSE },
864            { .name = "wlan", .gpio = 1 << 6, .polarity = REVERSE },
865        },
866    },
867    /* Netgear */
868    [WNR834BV2] = {
869        .name = "Netgear WNR834B V2",
870        .buttons = {
871            { .name = "reset", .gpio = 1 << 6 },
872        },
873        .leds = {
874            { .name = "power", .gpio = 1 << 2, .polarity = NORMAL },
875            { .name = "diag", .gpio = 1 << 3, .polarity = NORMAL },
876            { .name = "connected", .gpio = 1 << 7, .polarity = NORMAL },
877        },
878    },
879    /* Trendware */
880    [TEW411BRPP] = {
881        .name = "Trendware TEW411BRP+",
882        .buttons = {
883            { /* No usable buttons */ },
884        },
885        .leds = {
886            { .name = "power", .gpio = 1 << 7, .polarity = NORMAL },
887            { .name = "wlan", .gpio = 1 << 1, .polarity = NORMAL },
888            { .name = "bridge", .gpio = 1 << 6, .polarity = NORMAL },
889        },
890    },
891    /* SimpleTech */
892    [STI_NAS] = {
893        .name = "SimpleTech SimpleShare NAS",
894        .buttons = {
895            { .name = "reset", .gpio = 1 << 0 }, // Power button on back, named reset to enable failsafe.
896        },
897        .leds = {
898            { .name = "diag", .gpio = 1 << 1, .polarity = REVERSE }, // actual name ready
899        },
900        .platform_init = bcm4780_init,
901    },
902    /* D-Link */
903    [DIR130] = {
904        .name = "D-Link DIR-130",
905        .buttons = {
906            { .name = "reset", .gpio = 1 << 3},
907            { .name = "reserved", .gpio = 1 << 7},
908        },
909        .leds = {
910            { .name = "diag", .gpio = 1 << 0},
911            { .name = "blue", .gpio = 1 << 6},
912        },
913    },
914    [DIR320] = {
915        .name = "D-Link DIR-320",
916        .buttons = {
917            { .name = "reserved", .gpio = 1 << 6},
918            { .name = "reset", .gpio = 1 << 7},
919        },
920        .leds = {
921            { .name = "wlan", .gpio = 1 << 0, .polarity = NORMAL },
922            { .name = "diag", .gpio = 1 << 1, .polarity = NORMAL }, /* "status led */
923            { .name = "red", .gpio = 1 << 3, .polarity = REVERSE },
924            { .name = "blue", .gpio = 1 << 4, .polarity = REVERSE },
925            { .name = "usb", .gpio = 1 << 5, .polarity = NORMAL },
926        },
927    },
928    [DIR330] = {
929        .name = "D-Link DIR-330",
930        .buttons = {
931            { .name = "reset", .gpio = 1 << 3},
932            { .name = "reserved", .gpio = 1 << 7},
933        },
934        .leds = {
935            { .name = "diag", .gpio = 1 << 0},
936            { .name = "usb", .gpio = 1 << 4},
937            { .name = "blue", .gpio = 1 << 6},
938        },
939    },
940    [DWL3150] = {
941        .name = "D-Link DWL-3150",
942        .buttons = {
943            { .name = "reset", .gpio = 1 << 7},
944        },
945        .leds = {
946            { .name = "diag", .gpio = 1 << 2},
947            { .name = "status", .gpio = 1 << 1},
948        },
949    },
950    /* Double check */
951    [WL105B] = {
952        .name = "Sitecom WL-105b",
953        .buttons = {
954            { .name = "reset", .gpio = 1 << 10},
955        },
956        .leds = {
957            { .name = "wlan", .gpio = 1 << 4},
958            { .name = "power", .gpio = 1 << 3},
959        },
960    },
961    /* Western Digital Net Center */
962    [WDNetCenter] = {
963        .name = "Western Digital NetCenter",
964        .buttons = {
965            { .name = "power", .gpio = 1 << 0},
966            { .name = "reset", .gpio = 1 << 7},
967        },
968        .platform_init = NetCenter_init,
969    },
970    /* Askey (and clones) */
971    [RT210W] = {
972        .name = "Askey RT210W",
973        .buttons = {
974            /* Power button is hard-wired to hardware reset */
975            /* but is also connected to GPIO 7 (probably for bootloader recovery) */
976            { .name = "power", .gpio = 1 << 7},
977        },
978        .leds = {
979             /* These were verified and named based on Belkin F5D4230-4 v1112 */
980            { .name = "connected", .gpio = 1 << 0, .polarity = REVERSE },
981            { .name = "wlan", .gpio = 1 << 3, .polarity = REVERSE },
982            { .name = "power", .gpio = 1 << 5, .polarity = REVERSE },
983        },
984    },
985    [WL1600GL] = {
986        .name = "OvisLink WL-1600GL",
987        .buttons = {
988            { .name = "reset", .gpio = 1 << 3 },
989            { .name = "ses", .gpio = 1 << 4 },
990        },
991        .leds = {
992            { .name = "power", .gpio = 1 << 5, .polarity = REVERSE },
993            { .name = "wps", .gpio = 1 << 2, .polarity = REVERSE },
994            { .name = "wlan", .gpio = 1 << 1, .polarity = REVERSE },
995            { .name = "connected", .gpio = 1 << 0, .polarity = REVERSE },
996        },
997    },
998    /* Microsoft */
999    [MN700] = {
1000        .name = "Microsoft MN-700",
1001        .buttons = {
1002            { .name = "reset", .gpio = 1 << 7 },
1003        },
1004        .leds = {
1005            { .name = "power", .gpio = 1 << 6, .polarity = NORMAL },
1006        },
1007    },
1008    /* Edimax */
1009    [PS1208MFG] = {
1010        .name = "Edimax PS-1208MFG",
1011        .buttons = {
1012            { .name = "reset", .gpio = 1 << 4 },
1013        },
1014        .leds = {
1015            { .name = "status", .gpio = 1 << 1, .polarity = NORMAL },
1016            { .name = "wlan", .gpio = 1 << 0, .polarity = NORMAL },
1017        },
1018    },
1019};
1020
1021static struct platform_t __init *platform_detect(void)
1022{
1023    char *boardnum, *boardtype, *buf;
1024
1025    if (strcmp(getvar("nvram_type"), "cfe") == 0)
1026        return &platforms[WGT634U];
1027
1028    /* Look for a model identifier */
1029
1030    /* Based on "model_name" */
1031    if ((buf = nvram_get("model_name"))) {
1032        if (!strcmp(buf, "DIR-130"))
1033            return &platforms[DIR130];
1034        if (!strcmp(buf, "DIR-330"))
1035            return &platforms[DIR330];
1036    }
1037
1038    /* Based on "wsc_modelname */
1039    if ((buf = nvram_get("wsc_modelname"))) {
1040        if (!strcmp(buf, "WRT610N"))
1041            return &platforms[WRT610N];
1042    }
1043
1044    /* Based on "model_no" */
1045    if ((buf = nvram_get("model_no"))) {
1046        if (startswith(buf,"WL700")) /* WL700* */
1047            return &platforms[WL700GE];
1048    }
1049
1050    /* Based on "hardware_version" */
1051    if ((buf = nvram_get("hardware_version"))) {
1052        if (startswith(buf,"WL500GPV2-")) /* WL500GPV2-* */
1053            return &platforms[WL500GPV2];
1054        if (startswith(buf,"WL520GC-")) /* WL520GU-* */
1055            return &platforms[WL520GC];
1056        if (startswith(buf,"WL520GU-")) /* WL520GU-* */
1057            return &platforms[WL520GU];
1058        if (startswith(buf,"WL330GE-")) /* WL330GE-* */
1059            return &platforms[WL330GE];
1060        if (startswith(buf,"RT-N16-")) /* RT-N16-* */
1061            return &platforms[RTN16];
1062        if (startswith(buf,"F7D4301")) /* F7D4301* */
1063            return &platforms[BELKIN_F7D4301];
1064    }
1065
1066    /* Based on "ModelId" */
1067    if ((buf = nvram_get("ModelId"))) {
1068        if (!strcmp(buf, "WR850GP"))
1069            return &platforms[WR850GP];
1070        if (!strcmp(buf, "WR850G"))
1071            return &platforms[WR850GV2V3];
1072        if (!strcmp(buf, "WX-5565") && !strcmp(getvar("boardtype"),"bcm94710ap"))
1073            return &platforms[TM2300]; /* Dell TrueMobile 2300 */
1074        if (startswith(buf,"WE800G")) /* WE800G* */
1075            return &platforms[WE800G];
1076    }
1077
1078    /* Buffalo */
1079    if ((buf = (nvram_get("melco_id") ?: nvram_get("buffalo_id")))) {
1080        /* Buffalo hardware, check id for specific hardware matches */
1081        if (!strcmp(buf, "29bb0332"))
1082            return &platforms[WBR2_G54];
1083        if (!strcmp(buf, "29129"))
1084            return &platforms[WLA2_G54L];
1085        if (!strcmp(buf, "30189"))
1086            return &platforms[WHR_HP_G54];
1087        if (!strcmp(buf, "32093"))
1088            return &platforms[WHR_G125];
1089        if (!strcmp(buf, "30182"))
1090            return &platforms[WHR_G54S];
1091        if (!strcmp(buf, "290441dd"))
1092            return &platforms[WHR2_A54G54];
1093        if (!strcmp(buf, "31120"))
1094            return &platforms[WZR_G300N];
1095        if (!strcmp(buf, "30083"))
1096            return &platforms[WZR_RS_G54];
1097        if (!strcmp(buf, "30103"))
1098            return &platforms[WZR_RS_G54HP];
1099    }
1100
1101    /* no easy model number, attempt to guess */
1102    boardnum = getvar("boardnum");
1103    boardtype = getvar("boardtype");
1104
1105    if (!strcmp(boardnum, "20070615")) { /* Linksys WRT600N v1/V1.1 */
1106        if (!strcmp(boardtype, "0x478") && !strcmp(getvar("cardbus"), "0") && !strcmp(getvar("switch_type"),"BCM5395"))
1107            return &platforms[WRT600NV11];
1108
1109    if (!strcmp(boardtype, "0x478") && !strcmp(getvar("cardbus"), "0"))
1110            return &platforms[WRT600N];
1111    }
1112
1113    /*
1114     * Normally, these would go inside the "CFE based - newer hardware" block below; however, during early init, the
1115     * "pmon_ver" variable is not available on the E3000v1 (and probably the WRT610Nv2 also). Until this is figured out,
1116     * these will need to remain here in order for platform detection to work.
1117     */
1118    if (!strcmp(boardnum, "42")) { /* Linksys */
1119        if (!strcmp(boardtype, "0x04cf") && !strcmp(getvar("boot_hw_model"), "E300") && !strcmp(getvar("boot_hw_ver"), "1.0"))
1120            return &platforms[E3000V1];
1121
1122        if (!strcmp(boardtype, "0x04cf") && !strcmp(getvar("boot_hw_model"), "WRT610N") && !strcmp(getvar("boot_hw_ver"), "2.0"))
1123            return &platforms[WRT610NV2];
1124    }
1125
1126    if (startswith(getvar("pmon_ver"), "CFE")) {
1127        /* CFE based - newer hardware */
1128        if (!strcmp(boardnum, "42")) { /* Linksys */
1129            if (!strcmp(boardtype, "0x478") && !strcmp(getvar("boot_hw_model"), "WRT300N") && !strcmp(getvar("boot_hw_ver"), "1.1"))
1130                return &platforms[WRT300NV11];
1131
1132            if (!strcmp(boardtype, "0x478") && !strcmp(getvar("cardbus"), "1"))
1133                return &platforms[WRT350N];
1134
1135            if (!strcmp(boardtype, "0x0101") && !strcmp(getvar("boot_ver"), "v3.6"))
1136                return &platforms[WRT54G3G];
1137
1138            if (!strcmp(boardtype, "0x042f") && !strcmp(getvar("model_name"), "WRT54G3GV2-VF"))
1139                return &platforms[WRT54G3GV2_VF];
1140
1141            if (!strcmp(getvar("et1phyaddr"),"5") && !strcmp(getvar("et1mdcport"), "1"))
1142                return &platforms[WRTSL54GS];
1143
1144            if (!strcmp(boardtype, "0x0472")) {
1145                if(!strcmp(getvar("boot_hw_model"), "WRT150N")) {
1146                    if(!strcmp(getvar("boot_hw_ver"), "1"))
1147                        return &platforms[WRT150NV1];
1148                    else if(!strcmp(getvar("boot_hw_ver"), "1.1"))
1149                        return &platforms[WRT150NV11];
1150                }
1151                else if(!strcmp(getvar("boot_hw_model"), "WRT160N")) {
1152                    if(!strcmp(getvar("boot_hw_ver"), "1.0"))
1153                        return &platforms[WRT160NV1];
1154                    else if(!strcmp(getvar("boot_hw_ver"), "3.0"))
1155                        return &platforms[WRT160NV3];
1156                }
1157            }
1158
1159            /* default to WRT54G */
1160            return &platforms[WRT54G];
1161        }
1162        if (!strcmp(boardnum, "1024") && !strcmp(boardtype, "0x0446"))
1163            return &platforms[WAP54GV2];
1164
1165        if (!strcmp(boardnum, "8") && !strcmp(boardtype, "0x048e"))
1166            return &platforms[WL1600GL];
1167
1168
1169        if (!strcmp(boardnum, "44") || !strcmp(boardnum, "44\r")) {
1170            if (!strcmp(boardtype,"0x0101") || !strcmp(boardtype, "0x0101\r"))
1171                return &platforms[TM2300V2]; /* Dell TrueMobile 2300 v2 */
1172        }
1173
1174        if (!strcmp(boardnum, "45")) { /* ASUS */
1175            if (!strcmp(boardtype,"0x042f"))
1176                return &platforms[WL500GP];
1177            else if (!strcmp(boardtype,"0x0472"))
1178                return &platforms[WL500W];
1179            else if (!strcmp(boardtype,"0x467"))
1180                return &platforms[WL320GE];
1181            else
1182                return &platforms[WL500GD];
1183        }
1184
1185        if (!strcmp(boardnum, "10496"))
1186            return &platforms[USR5461];
1187
1188        if (!strcmp(getvar("boardtype"), "0x0101") && !strcmp(getvar("boardrev"), "0x10")) /* SE505V2 With Modified CFE */
1189            return &platforms[SE505V2];
1190
1191        if (!strcmp(boardtype, "0x048e") && !strcmp(getvar("boardrev"),"0x35") &&
1192                !strcmp(getvar("boardflags"), "0x750")) /* D-Link DIR-320 */
1193            return &platforms[DIR320];
1194
1195        if (!strncmp(boardnum, "TH",2) && !strcmp(boardtype,"0x042f")) {
1196            return &platforms[WDNetCenter];
1197        }
1198
1199        if (!strcmp(boardtype,"0x0472") && !strcmp(getvar("cardbus"), "1")) { /* Netgear WNR834B V1 and V2*/
1200            if (!strcmp(boardnum, "08") || !strcmp(boardnum, "8"))
1201                return &platforms[WNR834BV1];
1202            if (!strcmp(boardnum, "01") || !strcmp(boardnum, "1"))
1203                return &platforms[WNR834BV2];
1204        }
1205
1206    } else { /* PMON based - old stuff */
1207        if ((simple_strtoul(getvar("GemtekPmonVer"), NULL, 0) == 9) &&
1208            (simple_strtoul(getvar("et0phyaddr"), NULL, 0) == 30)) {
1209            return &platforms[WR850GV1];
1210        }
1211        if (startswith(boardtype, "bcm94710dev")) {
1212            if (!strcmp(boardnum, "42"))
1213                return &platforms[WRT54GV1];
1214            if (simple_strtoul(boardnum, NULL, 0) == 2)
1215                return &platforms[WAP54GV1];
1216        }
1217        /* MN-700 has also hardware_version 'WL500-...', so use boardnum */
1218        if (startswith(getvar("hardware_version"), "WL500-")) {
1219            if (!strcmp(getvar("boardnum"), "mn700"))
1220                return &platforms[MN700];
1221            else
1222                return &platforms[WL500G];
1223        }
1224        if (startswith(getvar("hardware_version"), "WL300-")) {
1225            /* Either WL-300g or WL-HDD, do more extensive checks */
1226            if ((simple_strtoul(getvar("et0phyaddr"), NULL, 0) == 0) &&
1227                (simple_strtoul(getvar("et1phyaddr"), NULL, 0) == 1))
1228                return &platforms[WLHDD];
1229            if ((simple_strtoul(getvar("et0phyaddr"), NULL, 0) == 0) &&
1230                (simple_strtoul(getvar("et1phyaddr"), NULL, 0) == 10))
1231                return &platforms[WL300G];
1232        }
1233        /* Sitecom WL-105b */
1234        if (startswith(boardnum, "2") && simple_strtoul(getvar("GemtekPmonVer"), NULL, 0) == 1)
1235            return &platforms[WL105B];
1236
1237        /* unknown asus stuff, probably bcm4702 */
1238        if (startswith(boardnum, "asusX"))
1239            return &platforms[ASUS_4702];
1240
1241        /* bcm4702 based Askey RT210W clones, Including:
1242         * Askey RT210W (duh?)
1243         * Siemens SE505v1
1244         * Belkin F5D7230-4 before version v1444 (MiniPCI slot, not integrated)
1245         */
1246        if (!strcmp(boardtype,"bcm94710r4")
1247         && !strcmp(boardnum ,"100")
1248         && !strcmp(getvar("pmon_ver"),"v1.03.12.bk")
1249           ){
1250            return &platforms[RT210W];
1251        }
1252    }
1253
1254    if (buf || !strcmp(boardnum, "00")) {/* probably buffalo */
1255        if (startswith(boardtype, "bcm94710ap"))
1256            return &platforms[BUFFALO_UNKNOWN_4710];
1257        else
1258            return &platforms[BUFFALO_UNKNOWN];
1259    }
1260
1261    if (startswith(getvar("CFEver"), "MotoWRv2") ||
1262        startswith(getvar("CFEver"), "MotoWRv3") ||
1263        !strcmp(getvar("MOTO_BOARD_TYPE"), "WR_FEM1")) {
1264
1265        return &platforms[WR850GV2V3];
1266    }
1267
1268    if (!strcmp(boardnum, "44") && !strcmp(getvar("boardflags"),"0x0388")) { /* Trendware TEW-411BRP+ */
1269        return &platforms[TEW411BRPP];
1270    }
1271
1272    if (startswith(boardnum, "04FN")) /* SimpleTech SimpleShare */
1273        return &platforms[STI_NAS];
1274
1275    if (!strcmp(boardnum, "10") && !strcmp(getvar("boardrev"), "0x13")) /* D-Link DWL-3150 */
1276        return &platforms[DWL3150];
1277
1278    if (!strcmp(boardnum, "01") && !strcmp(boardtype, "0x048e") && /* Edimax PS1208MFG */
1279        !strcmp(getvar("status_gpio"), "1")) /* gpio based detection */
1280        return &platforms[PS1208MFG];
1281
1282    /* not found */
1283    return NULL;
1284}
1285
1286static inline void ssb_maskset32(struct ssb_device *dev,
1287                  u16 offset, u32 mask, u32 set)
1288{
1289    ssb_write32(dev, offset, (ssb_read32(dev, offset) & mask) | set);
1290}
1291
1292static void gpio_set_irqenable(int enabled, irqreturn_t (*handler)(int, void *))
1293{
1294    int irq;
1295
1296    irq = gpio_to_irq(0);
1297    if (irq == -EINVAL) return;
1298    
1299    if (enabled) {
1300        if (request_irq(irq, handler, IRQF_SHARED, "gpio", handler))
1301            return;
1302    } else {
1303        free_irq(irq, handler);
1304    }
1305
1306    switch (bcm47xx_bus_type) {
1307#ifdef CONFIG_BCM47XX_SSB
1308    case BCM47XX_BUS_TYPE_SSB:
1309        if (bcm47xx_bus.ssb.chipco.dev)
1310            ssb_maskset32(bcm47xx_bus.ssb.chipco.dev, SSB_CHIPCO_IRQMASK, ~SSB_CHIPCO_IRQ_GPIO, (enabled ? SSB_CHIPCO_IRQ_GPIO : 0));
1311        break;
1312#endif
1313#ifdef CONFIG_BCM47XX_BCMA
1314    case BCM47XX_BUS_TYPE_BCMA:
1315        if (bcm47xx_bus.bcma.bus.drv_cc.core)
1316            bcma_maskset32(bcm47xx_bus.bcma.bus.drv_cc.core, BCMA_CC_IRQMASK, ~BCMA_CC_IRQ_GPIO, (enabled ? BCMA_CC_IRQ_GPIO : 0));
1317        break;
1318#endif
1319    }
1320}
1321
1322static void register_buttons(struct button_t *b)
1323{
1324    for (; b->name; b++)
1325        platform.button_mask |= b->gpio;
1326
1327    platform.button_mask &= ~gpiomask;
1328
1329    bcm47xx_gpio_outen(platform.button_mask, 0);
1330    bcm47xx_gpio_control(platform.button_mask, 0);
1331    platform.button_polarity = bcm47xx_gpio_in(~0) & platform.button_mask;
1332    bcm47xx_gpio_polarity(platform.button_mask, platform.button_polarity);
1333    bcm47xx_gpio_intmask(platform.button_mask, platform.button_mask);
1334
1335    gpio_set_irqenable(1, button_handler);
1336}
1337
1338static void unregister_buttons(struct button_t *b)
1339{
1340    bcm47xx_gpio_intmask(platform.button_mask, 0);
1341
1342    gpio_set_irqenable(0, button_handler);
1343}
1344
1345
1346static void add_msg(struct event_t *event, char *msg, int argv)
1347{
1348    char *s;
1349
1350    if (argv)
1351        return;
1352
1353    s = skb_put(event->skb, strlen(msg) + 1);
1354    strcpy(s, msg);
1355}
1356
1357static void hotplug_button(struct work_struct *work)
1358{
1359    struct event_t *event = container_of(work, struct event_t, wq);
1360    char *s;
1361
1362    event->skb = alloc_skb(2048, GFP_KERNEL);
1363
1364    s = skb_put(event->skb, strlen(event->action) + 2);
1365    sprintf(s, "%s@", event->action);
1366    fill_event(event);
1367
1368    NETLINK_CB(event->skb).dst_group = 1;
1369    broadcast_uevent(event->skb, 0, 1, GFP_KERNEL);
1370
1371    kfree(event);
1372}
1373
1374
1375static int fill_event (struct event_t *event)
1376{
1377    static char buf[128];
1378
1379    add_msg(event, "HOME=/", 0);
1380    add_msg(event, "PATH=/sbin:/bin:/usr/sbin:/usr/bin", 0);
1381    add_msg(event, "SUBSYSTEM=button", 0);
1382    snprintf(buf, 128, "ACTION=%s", event->action);
1383    add_msg(event, buf, 0);
1384    snprintf(buf, 128, "BUTTON=%s", event->name);
1385    add_msg(event, buf, 0);
1386    snprintf(buf, 128, "SEEN=%ld", event->seen);
1387    add_msg(event, buf, 0);
1388    snprintf(buf, 128, "SEQNUM=%llu", uevent_next_seqnum());
1389    add_msg(event, buf, 0);
1390
1391    return 0;
1392}
1393
1394
1395static irqreturn_t button_handler(int irq, void *dev_id)
1396{
1397    struct button_t *b;
1398    u32 in, changed;
1399
1400    in = bcm47xx_gpio_in(~0) & platform.button_mask;
1401    bcm47xx_gpio_polarity(platform.button_mask, in);
1402    changed = platform.button_polarity ^ in;
1403    platform.button_polarity = in;
1404
1405    changed &= ~bcm47xx_gpio_outen(0, 0);
1406
1407    for (b = platform.buttons; b->name; b++) {
1408        struct event_t *event;
1409
1410        if (!(b->gpio & changed)) continue;
1411
1412        b->pressed ^= 1;
1413
1414        if ((event = (struct event_t *)kzalloc (sizeof(struct event_t), GFP_ATOMIC))) {
1415            event->seen = (jiffies - b->seen)/HZ;
1416            event->name = b->name;
1417            event->action = b->pressed ? "pressed" : "released";
1418            INIT_WORK(&event->wq, (void *)(void *)hotplug_button);
1419            schedule_work(&event->wq);
1420        }
1421
1422        b->seen = jiffies;
1423    }
1424    return IRQ_HANDLED;
1425}
1426
1427static void register_leds(struct led_t *l)
1428{
1429    struct proc_dir_entry *p;
1430    u32 mask = 0;
1431    u32 oe_mask = 0;
1432    u32 val = 0;
1433
1434    leds = proc_mkdir("led", diag);
1435    if (!leds)
1436        return;
1437
1438    for(; l->name; l++) {
1439        if (l->gpio & gpiomask)
1440            continue;
1441
1442        if (l->gpio & GPIO_TYPE_EXTIF) {
1443            l->state = 0;
1444            set_led_extif(l);
1445        } else {
1446            if (l->polarity != INPUT) oe_mask |= l->gpio;
1447            mask |= l->gpio;
1448            val |= (l->polarity == NORMAL)?0:l->gpio;
1449        }
1450
1451        if (l->polarity == INPUT) continue;
1452
1453        if ((p = create_proc_entry(l->name, S_IRUSR, leds))) {
1454            l->proc.type = PROC_LED;
1455            l->proc.ptr = l;
1456            p->data = (void *) &l->proc;
1457            p->proc_fops = &diag_proc_fops;
1458        }
1459    }
1460
1461    bcm47xx_gpio_outen(mask, oe_mask);
1462    bcm47xx_gpio_control(mask, 0);
1463    bcm47xx_gpio_out(mask, val);
1464    bcm47xx_gpio_intmask(mask, 0);
1465}
1466
1467static void unregister_leds(struct led_t *l)
1468{
1469    for(; l->name; l++)
1470        remove_proc_entry(l->name, leds);
1471
1472    remove_proc_entry("led", diag);
1473}
1474
1475static void set_led_extif(struct led_t *led)
1476{
1477    volatile u8 *addr = (volatile u8 *) KSEG1ADDR(EXTIF_UART) + (led->gpio & ~GPIO_TYPE_MASK);
1478    if (led->state)
1479        *addr = 0xFF;
1480    else
1481        *addr;
1482}
1483
1484static void led_flash(unsigned long dummy) {
1485    struct led_t *l;
1486    u32 mask = 0;
1487    u8 extif_blink = 0;
1488
1489    for (l = platform.leds; l->name; l++) {
1490        if (l->flash) {
1491            if (l->gpio & GPIO_TYPE_EXTIF) {
1492                extif_blink = 1;
1493                l->state = !l->state;
1494                set_led_extif(l);
1495            } else {
1496                mask |= l->gpio;
1497            }
1498        }
1499    }
1500
1501    mask &= ~gpiomask;
1502    if (mask) {
1503        u32 val = ~bcm47xx_gpio_in(~0);
1504
1505        bcm47xx_gpio_outen(mask, mask);
1506        bcm47xx_gpio_control(mask, 0);
1507        bcm47xx_gpio_out(mask, val);
1508    }
1509    if (mask || extif_blink) {
1510        mod_timer(&led_timer, jiffies + FLASH_TIME);
1511    }
1512}
1513
1514static ssize_t diag_proc_read(struct file *file, char *buf, size_t count, loff_t *ppos)
1515{
1516    struct proc_dir_entry *dent = PDE(file->f_dentry->d_inode);
1517    char *page;
1518    int len = 0;
1519
1520    if ((page = kmalloc(1024, GFP_KERNEL)) == NULL)
1521        return -ENOBUFS;
1522
1523    if (dent->data != NULL) {
1524        struct prochandler_t *handler = (struct prochandler_t *) dent->data;
1525        switch (handler->type) {
1526            case PROC_LED: {
1527                struct led_t * led = (struct led_t *) handler->ptr;
1528                if (led->flash) {
1529                    len = sprintf(page, "f\n");
1530                } else {
1531                    if (led->gpio & GPIO_TYPE_EXTIF) {
1532                        len = sprintf(page, "%d\n", led->state);
1533                    } else {
1534                        u32 in = (bcm47xx_gpio_in(~0) & led->gpio ? 1 : 0);
1535                        u8 p = (led->polarity == NORMAL ? 0 : 1);
1536                        len = sprintf(page, "%d\n", ((in ^ p) ? 1 : 0));
1537                    }
1538                }
1539                break;
1540            }
1541            case PROC_MODEL:
1542                len = sprintf(page, "%s\n", platform.name);
1543                break;
1544            case PROC_GPIOMASK:
1545                len = sprintf(page, "0x%04x\n", gpiomask);
1546                break;
1547        }
1548    }
1549    len += 1;
1550
1551    if (*ppos < len) {
1552        len = min_t(int, len - *ppos, count);
1553        if (copy_to_user(buf, (page + *ppos), len)) {
1554            kfree(page);
1555            return -EFAULT;
1556        }
1557        *ppos += len;
1558    } else {
1559        len = 0;
1560    }
1561
1562    kfree(page);
1563    return len;
1564}
1565
1566
1567static ssize_t diag_proc_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
1568{
1569    struct proc_dir_entry *dent = PDE(file->f_dentry->d_inode);
1570    char *page;
1571    int ret = -EINVAL;
1572
1573    if ((page = kmalloc(count + 1, GFP_KERNEL)) == NULL)
1574        return -ENOBUFS;
1575
1576    if (copy_from_user(page, buf, count)) {
1577        kfree(page);
1578        return -EINVAL;
1579    }
1580    page[count] = 0;
1581
1582    if (dent->data != NULL) {
1583        struct prochandler_t *handler = (struct prochandler_t *) dent->data;
1584        switch (handler->type) {
1585            case PROC_LED: {
1586                struct led_t *led = (struct led_t *) handler->ptr;
1587                int p = (led->polarity == NORMAL ? 0 : 1);
1588
1589                if (page[0] == 'f') {
1590                    led->flash = 1;
1591                    led_flash(0);
1592                } else {
1593                    led->flash = 0;
1594                    if (led->gpio & GPIO_TYPE_EXTIF) {
1595                        led->state = p ^ ((page[0] == '1') ? 1 : 0);
1596                        set_led_extif(led);
1597                    } else {
1598                        bcm47xx_gpio_outen(led->gpio, led->gpio);
1599                        bcm47xx_gpio_control(led->gpio, 0);
1600                        bcm47xx_gpio_out(led->gpio, ((p ^ (page[0] == '1')) ? led->gpio : 0));
1601                    }
1602                }
1603                break;
1604            }
1605            case PROC_GPIOMASK:
1606                gpiomask = simple_strtoul(page, NULL, 0);
1607
1608                if (platform.buttons) {
1609                    unregister_buttons(platform.buttons);
1610                    register_buttons(platform.buttons);
1611                }
1612
1613                if (platform.leds) {
1614                    unregister_leds(platform.leds);
1615                    register_leds(platform.leds);
1616                }
1617                break;
1618        }
1619        ret = count;
1620    }
1621
1622    kfree(page);
1623    return ret;
1624}
1625
1626static int __init diag_init(void)
1627{
1628    static struct proc_dir_entry *p;
1629    static struct platform_t *detected;
1630
1631    detected = platform_detect();
1632    if (!detected) {
1633        printk(MODULE_NAME ": Router model not detected.\n");
1634        return -ENODEV;
1635    }
1636    memcpy(&platform, detected, sizeof(struct platform_t));
1637
1638    printk(MODULE_NAME ": Detected '%s'\n", platform.name);
1639    if (platform.platform_init != NULL) {
1640        platform.platform_init();
1641    }
1642
1643    if (!(diag = proc_mkdir("diag", NULL))) {
1644        printk(MODULE_NAME ": proc_mkdir on /proc/diag failed\n");
1645        return -EINVAL;
1646    }
1647
1648    if ((p = create_proc_entry("model", S_IRUSR, diag))) {
1649        p->data = (void *) &proc_model;
1650        p->proc_fops = &diag_proc_fops;
1651    }
1652
1653    if ((p = create_proc_entry("gpiomask", S_IRUSR | S_IWUSR, diag))) {
1654        p->data = (void *) &proc_gpiomask;
1655        p->proc_fops = &diag_proc_fops;
1656    }
1657
1658    if (platform.buttons)
1659        register_buttons(platform.buttons);
1660
1661    if (platform.leds)
1662        register_leds(platform.leds);
1663
1664    return 0;
1665}
1666
1667static void __exit diag_exit(void)
1668{
1669    del_timer(&led_timer);
1670
1671    if (platform.buttons)
1672        unregister_buttons(platform.buttons);
1673
1674    if (platform.leds)
1675        unregister_leds(platform.leds);
1676
1677    remove_proc_entry("model", diag);
1678    remove_proc_entry("gpiomask", diag);
1679    remove_proc_entry("diag", NULL);
1680}
1681
1682module_init(diag_init);
1683module_exit(diag_exit);
1684
1685MODULE_AUTHOR("Mike Baker, Felix Fietkau / OpenWrt.org");
1686MODULE_LICENSE("GPL");
1687

Archive Download this file



interactive