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

Archive Download this file



interactive