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