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