0b074673e8ef02afbdd4cae0c14b90617d4a1b27
[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  *                    Felix Fietkau <nbd@openwrt.org>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *
21  * $Id$
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 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
32 #include <linux/kobject.h>
33 #include <linux/workqueue.h>
34 #define hotplug_path uevent_helper
35 #else
36 #include <linux/tqueue.h>
37 #define INIT_WORK INIT_TQUEUE
38 #define schedule_work schedule_task
39 #define work_struct tq_struct
40 #endif
41
42 #include "gpio.h"
43 #include "diag.h"
44 #define getvar(str) (nvram_get(str)?:"")
45
46 static unsigned int gpiomask = 0;
47 module_param(gpiomask, int, 0644);
48
49 enum {
50         /* Linksys */
51         WAP54GV1,
52         WAP54GV3,
53         WRT54GV1,
54         WRT54G,
55         WRTSL54GS,
56         WRT54G3G,
57         WRT350N,
58         
59         /* ASUS */
60         WLHDD,
61         WL300G,
62         WL500G,
63         WL500GD,
64         WL500GP,
65         WL500W,
66         ASUS_4702,
67         WL700GE,
68         
69         /* Buffalo */
70         WBR2_G54,
71         WHR_G54S,
72         WHR_HP_G54,
73         WHR2_A54G54,
74         WLA2_G54L,
75         WZR_G300N,
76         WZR_RS_G54,
77         WZR_RS_G54HP,
78         BUFFALO_UNKNOWN,
79         BUFFALO_UNKNOWN_4710,
80
81         /* Siemens */
82         SE505V1,
83         SE505V2,
84         
85         /* US Robotics */
86         USR5461,
87
88         /* Dell */
89         TM2300,
90
91         /* Motorola */
92         WE800G,
93         WR850GV1,
94         WR850GV2V3,
95
96         /* Belkin */
97         BELKIN_UNKNOWN,
98
99         /* Netgear */
100         WGT634U,
101
102         /* Trendware */
103         TEW411BRPP,
104         
105         /* SimpleTech */
106         STI_NAS,
107
108         /* D-Link */
109         DIR130,
110         DIR330,
111 };
112
113 static void __init bcm4780_init(void) {
114                 int pin = 1 << 3;
115
116                 /* Enables GPIO 3 that controls HDD and led power on ASUS WL-700gE */
117                 printk(MODULE_NAME ": Spinning up HDD and enabling leds\n");
118                 gpio_outen(pin, pin);
119                 gpio_control(pin, 0);
120                 gpio_out(pin, pin);
121
122                 /* Wait 5s, so the HDD can spin up */
123                 set_current_state(TASK_INTERRUPTIBLE);
124                 schedule_timeout(HZ * 5);
125 }
126
127 static struct platform_t __initdata platforms[] = {
128         /* Linksys */
129         [WAP54GV1] = {
130                 .name           = "Linksys WAP54G V1",
131                 .buttons        = {
132                         { .name = "reset",      .gpio = 1 << 0 },
133                 },
134                 .leds           = { 
135                         { .name = "diag",       .gpio = 1 << 3 },
136                         { .name = "wlan",       .gpio = 1 << 4 },
137                 },
138         },
139         [WAP54GV3] = {
140                 .name           = "Linksys WAP54G V3",
141                 .buttons        = {
142                         /* FIXME: verify this */
143                         { .name = "reset",      .gpio = 1 << 7 },
144                         { .name = "ses",        .gpio = 1 << 0 },
145                 },
146                 .leds           = { 
147                         /* FIXME: diag? */
148                         { .name = "ses",        .gpio = 1 << 1 },
149                 },
150         },
151         [WRT54GV1] = {
152                 .name           = "Linksys WRT54G V1.x",
153                 .buttons        = {
154                         { .name = "reset",      .gpio = 1 << 6 },
155                 },
156                 .leds           = { 
157                         { .name = "diag",       .gpio = 0x13 | GPIO_TYPE_EXTIF, .polarity = NORMAL },
158                         { .name = "dmz",        .gpio = 0x12 | GPIO_TYPE_EXTIF, .polarity = NORMAL },
159                 },
160         },
161         [WRT54G] = {
162                 .name           = "Linksys WRT54G/GS/GL",
163                 .buttons        = {
164                         { .name = "reset",      .gpio = 1 << 6 },
165                         { .name = "ses",        .gpio = 1 << 4 },
166                 },
167                 .leds           = {
168                         { .name = "power",      .gpio = 1 << 1, .polarity = NORMAL },
169                         { .name = "dmz",        .gpio = 1 << 7, .polarity = REVERSE },
170                         { .name = "ses_white",  .gpio = 1 << 2, .polarity = REVERSE },
171                         { .name = "ses_orange", .gpio = 1 << 3, .polarity = REVERSE },
172                         { .name = "wlan",       .gpio = 1 << 0, .polarity = REVERSE },
173                 },
174         },
175         [WRTSL54GS] = {
176                 .name           = "Linksys WRTSL54GS",
177                 .buttons        = {
178                         { .name = "reset",      .gpio = 1 << 6 },
179                         { .name = "ses",        .gpio = 1 << 4 },
180                 },
181                 .leds           = {
182                         { .name = "power",      .gpio = 1 << 1, .polarity = NORMAL },
183                         { .name = "dmz",        .gpio = 1 << 0, .polarity = REVERSE },
184                         { .name = "ses_white",  .gpio = 1 << 5, .polarity = REVERSE },
185                         { .name = "ses_orange", .gpio = 1 << 7, .polarity = REVERSE },
186                 },
187         },
188         [WRT54G3G] = {
189                 .name           = "Linksys WRT54G3G",
190                 .buttons        = {
191                         { .name = "reset",      .gpio = 1 << 6 },
192                         { .name = "3g",         .gpio = 1 << 4 },
193                 },
194                 .leds           = {
195                         { .name = "power",      .gpio = 1 << 1, .polarity = NORMAL },
196                         { .name = "dmz",        .gpio = 1 << 7, .polarity = REVERSE },
197                         { .name = "3g_green",   .gpio = 1 << 2, .polarity = NORMAL },
198                         { .name = "3g_blue",    .gpio = 1 << 3, .polarity = NORMAL },
199                         { .name = "3g_blink",   .gpio = 1 << 5, .polarity = NORMAL },
200                 },
201         },
202         [WRT350N] = {
203                 .name           = "Linksys WRT350N",
204                 .buttons        = {
205                         { .name = "reset",      .gpio = 1 << 6 },
206                         { .name = "ses",        .gpio = 1 << 8 },
207                 },
208                 .leds           = {
209                         { .name = "power",      .gpio = 1 << 1, .polarity = NORMAL },
210                         { .name = "ses",        .gpio = 1 << 3, .polarity = REVERSE },
211                 },
212         },
213         /* Asus */
214         [WLHDD] = {
215                 .name           = "ASUS WL-HDD",
216                 .buttons        = {
217                         { .name = "reset",      .gpio = 1 << 6 },
218                 },
219                 .leds           = {
220                         { .name = "power",      .gpio = 1 << 0, .polarity = REVERSE },
221                         { .name = "usb",        .gpio = 1 << 2, .polarity = NORMAL },
222                 },
223         },
224         [WL300G] = {
225                 .name           = "ASUS WL-300g",
226                 .buttons        = {
227                         { .name = "reset",      .gpio = 1 << 6 },
228                 },
229                 .leds           = {
230                         { .name = "power",      .gpio = 1 << 0, .polarity = REVERSE },
231                 },
232         },
233         [WL500G] = {
234                 .name           = "ASUS WL-500g",
235                 .buttons        = {
236                         { .name = "reset",      .gpio = 1 << 6 },
237                 },
238                 .leds           = {
239                         { .name = "power",      .gpio = 1 << 0, .polarity = REVERSE },
240                 },
241         },
242         [WL500GD] = {
243                 .name           = "ASUS WL-500g Deluxe",
244                 .buttons        = {
245                         { .name = "reset",      .gpio = 1 << 6 },
246                 },
247                 .leds           = {
248                         { .name = "power",      .gpio = 1 << 0, .polarity = REVERSE },
249                 },
250         },
251         [WL500GP] = {
252                 .name           = "ASUS WL-500g Premium",
253                 .buttons        = {
254                         { .name = "reset",      .gpio = 1 << 0 },
255                         { .name = "ses",        .gpio = 1 << 4 },
256                 },
257                 .leds           = {
258                         { .name = "power",      .gpio = 1 << 1, .polarity = REVERSE },
259                 },
260         },
261         [WL500W] = {
262                 .name           = "ASUS WL-500W",
263                 .buttons        = {
264                         { .name = "reset",      .gpio = 1 << 6 },
265                         { .name = "ses",        .gpio = 1 << 7 },
266                 },
267                 .leds           = {
268                         { .name = "power",      .gpio = 1 << 5, .polarity = REVERSE },
269                 },
270         },
271         [ASUS_4702] = {
272                 .name           = "ASUS (unknown, BCM4702)",
273                 .buttons        = {
274                         { .name = "reset",      .gpio = 1 << 6 },
275                 },
276                 .leds           = {
277                         { .name = "power",      .gpio = 1 << 0, .polarity = REVERSE },
278                 },
279         },
280         [WL700GE] = {
281                 .name           = "ASUS WL-700gE",
282                 .buttons        = {
283                         { .name = "reset",      .gpio = 1 << 7 }, // on back, hardwired, always resets device regardless OS state
284                         { .name = "ses",        .gpio = 1 << 4 }, // on back, actual name ezsetup
285                         { .name = "power",      .gpio = 1 << 0 }, // on front
286                         { .name = "copy",       .gpio = 1 << 6 }, // on front
287                 },
288                 .leds           = {
289 #if 0
290                         // GPIO that controls power led also enables/disables some essential functions
291                         // - power to HDD
292                         // - switch leds
293                         { .name = "power",      .gpio = 1 << 3, .polarity = NORMAL },  // actual name power
294 #endif
295                         { .name = "diag",       .gpio = 1 << 1, .polarity = REVERSE }, // actual name ready
296                 },
297                 .platform_init = bcm4780_init,
298         },
299         /* Buffalo */
300         [WHR_G54S] = {
301                 .name           = "Buffalo WHR-G54S",
302                 .buttons        = {
303                         { .name = "reset",      .gpio = 1 << 4 },
304                         { .name = "bridge",     .gpio = 1 << 5 },
305                         { .name = "ses",        .gpio = 1 << 0 },
306                 },
307                 .leds           = {
308                         { .name = "diag",       .gpio = 1 << 7, .polarity = REVERSE },
309                         { .name = "internal",   .gpio = 1 << 3, .polarity = REVERSE },
310                         { .name = "ses",        .gpio = 1 << 6, .polarity = REVERSE },
311                         { .name = "bridge",     .gpio = 1 << 1, .polarity = REVERSE },
312                         { .name = "wlan",       .gpio = 1 << 2, .polarity = REVERSE },
313                 },
314         },
315         [WBR2_G54] = {
316                 .name           = "Buffalo WBR2-G54",
317                 /* FIXME: verify */
318                 .buttons        = {
319                         { .name = "reset",      .gpio = 1 << 7 },
320                 },
321                 .leds           = {
322                         { .name = "diag",       .gpio = 1 << 1, .polarity = REVERSE },
323                 },
324         },
325         [WHR_HP_G54] = {
326                 .name           = "Buffalo WHR-HP-G54",
327                 .buttons        = {
328                         { .name = "reset",      .gpio = 1 << 4 },
329                         { .name = "bridge",     .gpio = 1 << 5 },
330                         { .name = "ses",        .gpio = 1 << 0 },
331                 },
332                 .leds           = {
333                         { .name = "diag",       .gpio = 1 << 7, .polarity = REVERSE },
334                         { .name = "internal",   .gpio = 1 << 3, .polarity = REVERSE },
335                         { .name = "bridge",     .gpio = 1 << 1, .polarity = REVERSE },
336                         { .name = "ses",        .gpio = 1 << 6, .polarity = REVERSE },
337                         { .name = "wlan",       .gpio = 1 << 2, .polarity = REVERSE },
338                 },
339         },
340         [WHR2_A54G54] = {
341                 .name           = "Buffalo WHR2-A54G54",
342                 .buttons        = {
343                         { .name = "reset",      .gpio = 1 << 4 },
344                 },
345                 .leds           = {
346                         { .name = "diag",       .gpio = 1 << 7, .polarity = REVERSE },
347                 },
348         },
349         [WLA2_G54L] = {
350                 .name           = "Buffalo WLA2-G54L",
351                 /* FIXME: verify */
352                 .buttons        = {
353                         { .name = "reset",      .gpio = 1 << 7 },
354                 },
355                 .leds           = {
356                         { .name = "diag",       .gpio = 1 << 1, .polarity = REVERSE },
357                 },
358         },
359         [WZR_G300N] = {
360                 .name           = "Buffalo WZR-G300N",
361                 .buttons        = {
362                         { .name = "reset",      .gpio = 1 << 4 },
363                 },
364                 .leds           = {
365                         { .name = "diag",       .gpio = 1 << 7, .polarity = REVERSE },
366                         { .name = "bridge",     .gpio = 1 << 1, .polarity = REVERSE },
367                         { .name = "ses",        .gpio = 1 << 6, .polarity = REVERSE },
368                 },
369         },
370         [WZR_RS_G54] = {
371                 .name           = "Buffalo WZR-RS-G54",
372                 .buttons        = {
373                         { .name = "ses",        .gpio = 1 << 0 },
374                         { .name = "reset",      .gpio = 1 << 4 },
375                 },
376                 .leds           = {
377                         { .name = "diag",       .gpio = 1 << 7, .polarity = REVERSE },
378                         { .name = "ses",        .gpio = 1 << 6, .polarity = REVERSE },
379                         { .name = "vpn",        .gpio = 1 << 1, .polarity = REVERSE },
380                 },
381         },
382         [WZR_RS_G54HP] = {
383                 .name           = "Buffalo WZR-RS-G54HP",
384                 .buttons        = {
385                         { .name = "ses",        .gpio = 1 << 0 },
386                         { .name = "reset",      .gpio = 1 << 4 },
387                 },
388                 .leds           = {
389                         { .name = "diag",       .gpio = 1 << 7, .polarity = REVERSE },
390                         { .name = "ses",        .gpio = 1 << 6, .polarity = REVERSE },
391                         { .name = "vpn",        .gpio = 1 << 1, .polarity = REVERSE },
392                 },
393         },
394         [BUFFALO_UNKNOWN] = {
395                 .name           = "Buffalo (unknown)",
396                 .buttons        = {
397                         { .name = "reset",      .gpio = 1 << 7 },
398                 },
399                 .leds           = {
400                         { .name = "diag",       .gpio = 1 << 1, .polarity = REVERSE },
401                 },
402         },
403         [BUFFALO_UNKNOWN_4710] = {
404                 .name           = "Buffalo (unknown, BCM4710)",
405                 .buttons        = {
406                         { .name = "reset",      .gpio = 1 << 4 },
407                 },
408                 .leds           = {
409                         { .name = "diag",       .gpio = 1 << 1, .polarity = REVERSE },
410                 },
411         },
412         /* Siemens */
413         [SE505V1] = {
414                 .name           = "Siemens SE505 V1",
415                 .buttons        = {
416                         /* No usable buttons */
417                 },
418                 .leds           = {
419                         { .name = "dmz",        .gpio = 1 << 4, .polarity = REVERSE },
420                         { .name = "wlan",       .gpio = 1 << 3, .polarity = REVERSE },
421                 },
422         },
423         [SE505V2] = {
424                 .name           = "Siemens SE505 V2",
425                 .buttons        = {
426                         /* No usable buttons */
427                 },
428                 .leds           = {
429                         { .name = "power",      .gpio = 1 << 5, .polarity = REVERSE },
430                         { .name = "dmz",        .gpio = 1 << 0, .polarity = REVERSE },
431                         { .name = "wlan",       .gpio = 1 << 3, .polarity = REVERSE },
432                 },
433         },
434         /* US Robotics */
435         [USR5461] = {
436                 .name           = "U.S. Robotics USR5461",
437                 .buttons        = {
438                         /* No usable buttons */
439                 },
440                 .leds           = {
441                         { .name = "wlan",       .gpio = 1 << 0, .polarity = REVERSE },
442                         { .name = "printer",    .gpio = 1 << 1, .polarity = REVERSE },
443                 },
444         },
445         /* Dell */
446         [TM2300] = {
447                 .name           = "Dell TrueMobile 2300",
448                 .buttons        = {
449                         { .name = "reset",      .gpio = 1 << 0 },
450                 },
451                 .leds           = {
452                         { .name = "wlan",       .gpio = 1 << 6, .polarity = REVERSE },
453                         { .name = "power",      .gpio = 1 << 7, .polarity = REVERSE },
454                 },
455         },
456         /* Motorola */
457         [WE800G] = {
458                 .name           = "Motorola WE800G",
459                 .buttons        = {
460                         { .name = "reset",      .gpio = 1 << 0 },
461                 },
462                 .leds           = {
463                         { .name = "power",      .gpio = 1 << 4, .polarity = NORMAL },
464                         { .name = "diag",       .gpio = 1 << 2, .polarity = REVERSE },
465                         { .name = "wlan_amber", .gpio = 1 << 1, .polarity = NORMAL },
466                 },
467         },
468         [WR850GV1] = {
469                 .name           = "Motorola WR850G V1",
470                 .buttons        = {
471                         { .name = "reset",      .gpio = 1 << 0 },
472                 },
473                 .leds           = {
474                         { .name = "power",      .gpio = 1 << 4, .polarity = NORMAL },
475                         { .name = "diag",       .gpio = 1 << 3, .polarity = REVERSE },
476                         { .name = "dmz",        .gpio = 1 << 6, .polarity = NORMAL },
477                         { .name = "wlan_red",   .gpio = 1 << 5, .polarity = REVERSE },
478                         { .name = "wlan_green", .gpio = 1 << 7, .polarity = REVERSE },
479                 },
480         },
481         [WR850GV2V3] = {
482                 .name           = "Motorola WR850G V2/V3",
483                 .buttons        = {
484                         { .name = "reset",      .gpio = 1 << 5 },
485                 },
486                 .leds           = {
487                         { .name = "power",      .gpio = 1 << 1, .polarity = NORMAL },
488                         { .name = "wlan",       .gpio = 1 << 0, .polarity = REVERSE },
489                         { .name = "dmz",        .gpio = 1 << 6, .polarity = REVERSE },
490                         { .name = "diag",       .gpio = 1 << 7, .polarity = REVERSE },
491                 },
492         },
493         /* Belkin */
494         [BELKIN_UNKNOWN] = {
495                 .name           = "Belkin (unknown)",
496                 /* FIXME: verify & add detection */
497                 .buttons        = {
498                         { .name = "reset",      .gpio = 1 << 7 },
499                 },
500                 .leds           = {
501                         { .name = "power",      .gpio = 1 << 5, .polarity = NORMAL },
502                         { .name = "wlan",       .gpio = 1 << 3, .polarity = NORMAL },
503                         { .name = "connected",  .gpio = 1 << 0, .polarity = NORMAL },
504                 },
505         },
506         /* Netgear */
507         [WGT634U] = {
508                 .name           = "Netgear WGT634U",
509                 .buttons        = {
510                         { .name = "reset",      .gpio = 1 << 2 },
511                 },
512                 .leds           = {
513                         { .name = "power",      .gpio = 1 << 3, .polarity = NORMAL },
514                 },
515         },
516         /* Trendware */
517         [TEW411BRPP] = {
518                 .name           = "Trendware TEW411BRP+",
519                 .buttons        = {
520                         { /* No usable buttons */ },
521                 },
522                 .leds           = {
523                         { .name = "power",      .gpio = 1 << 7, .polarity = NORMAL },
524                         { .name = "wlan",       .gpio = 1 << 1, .polarity = NORMAL },
525                         { .name = "bridge",     .gpio = 1 << 6, .polarity = NORMAL },
526                 },
527         },
528         /* SimpleTech */
529         [STI_NAS] = {
530                 .name      = "SimpleTech SimpleShare NAS",
531                 .buttons        = {
532                         { .name = "reset",      .gpio = 1 << 7 }, // on back, hardwired, always resets device regardless OS state
533                         { .name = "power",      .gpio = 1 << 0 }, // on back
534                 },
535                 .leds      = {
536                         { .name = "diag",       .gpio = 1 << 1, .polarity = REVERSE }, // actual name ready
537                 },
538                 .platform_init = bcm4780_init,
539         },
540         /* D-Link */
541         [DIR130] = {
542                 .name     = "D-Link DIR-130",
543                 .buttons        = {
544                         { .name = "reset",      .gpio = 1 << 3},
545                         { .name = "reserved",   .gpio = 1 << 7},
546                 },
547                 .leds      = {
548                         { .name = "diag",       .gpio = 1 << 0},
549                         { .name = "blue",       .gpio = 1 << 6},
550                 },
551         },
552         [DIR330] = {
553                 .name     = "D-Link DIR-330",
554                 .buttons        = {
555                         { .name = "reset",      .gpio = 1 << 3},
556                         { .name = "reserved",   .gpio = 1 << 7},
557                 },
558                 .leds      = {
559                         { .name = "diag",       .gpio = 1 << 0},
560                         { .name = "usb",        .gpio = 1 << 4},
561                         { .name = "blue",       .gpio = 1 << 6},
562                 },
563         },
564 };
565
566 static struct platform_t __init *platform_detect(void)
567 {
568         char *boardnum, *boardtype, *buf;
569
570         boardnum = getvar("boardnum");
571         boardtype = getvar("boardtype");
572
573         if (strcmp(getvar("nvram_type"), "cfe") == 0)
574                 return &platforms[WGT634U];
575         
576         if (strncmp(getvar("model_no"), "WL700",5) == 0)
577                 return &platforms[WL700GE];
578
579         if (strncmp(getvar("pmon_ver"), "CFE", 3) == 0) {
580                 /* CFE based - newer hardware */
581                 if (!strcmp(boardnum, "42")) { /* Linksys */
582                         if (!strcmp(boardtype, "0x478") && !strcmp(getvar("cardbus"), "1"))
583                                 return &platforms[WRT350N];
584
585                         if (!strcmp(boardtype, "0x0101") && !strcmp(getvar("boot_ver"), "v3.6"))
586                                 return &platforms[WRT54G3G];
587
588                         if (!strcmp(getvar("et1phyaddr"),"5") && !strcmp(getvar("et1mdcport"), "1"))
589                                 return &platforms[WRTSL54GS];
590                         
591                         /* default to WRT54G */
592                         return &platforms[WRT54G];
593                 }
594                 
595                 if (!strcmp(boardnum, "45")) { /* ASUS */
596                         if (!strcmp(boardtype,"0x042f"))
597                                 return &platforms[WL500GP];
598                         else if (!strcmp(boardtype,"0x0472"))
599                                 return &platforms[WL500W];
600                         else
601                                 return &platforms[WL500GD];
602                 }
603                 
604                 if (!strcmp(boardnum, "10496"))
605                         return &platforms[USR5461];
606
607                 /* D-Link */
608                 if (!strcmp(getvar("model_name"), "DIR-130"))
609                         return &platforms[DIR130];
610                 if (!strcmp(getvar("model_name"), "DIR-330"))
611                         return &platforms[DIR330];
612
613         } else { /* PMON based - old stuff */
614
615                 /* Dell TrueMobile 2300 */
616                 if (!strcmp(getvar("ModelId"),"WX-5565"))
617                         return &platforms[TM2300];
618         
619                 if ((simple_strtoul(getvar("GemtekPmonVer"), NULL, 0) == 9) &&
620                         (simple_strtoul(getvar("et0phyaddr"), NULL, 0) == 30)) {
621                         if (!strncmp(getvar("ModelId"),"WE800G", 6))
622                                 return &platforms[WE800G];
623                         else
624                                 return &platforms[WR850GV1];
625                 }
626                 if (!strncmp(boardtype, "bcm94710dev", 11)) {
627                         if (!strcmp(boardnum, "42"))
628                                 return &platforms[WRT54GV1];
629                         if (simple_strtoul(boardnum, NULL, 0) == 2)
630                                 return &platforms[WAP54GV1];
631                 }
632                 if (!strncmp(getvar("hardware_version"), "WL500-", 6))
633                         return &platforms[WL500G];
634                 if (!strncmp(getvar("hardware_version"), "WL300-", 6)) {
635                         /* Either WL-300g or WL-HDD, do more extensive checks */
636                         if ((simple_strtoul(getvar("et0phyaddr"), NULL, 0) == 0) &&
637                                 (simple_strtoul(getvar("et1phyaddr"), NULL, 0) == 1))
638                                 return &platforms[WLHDD];
639                         if ((simple_strtoul(getvar("et0phyaddr"), NULL, 0) == 0) &&
640                                 (simple_strtoul(getvar("et1phyaddr"), NULL, 0) == 10))
641                                 return &platforms[WL300G];
642                 }
643
644                 /* unknown asus stuff, probably bcm4702 */
645                 if (!strncmp(boardnum, "asusX", 5))
646                         return &platforms[ASUS_4702];
647         }
648
649         if ((buf = (nvram_get("melco_id") ?: nvram_get("buffalo_id")))) {
650                 /* Buffalo hardware, check id for specific hardware matches */
651                 if (!strcmp(buf, "29bb0332"))
652                         return &platforms[WBR2_G54];
653                 if (!strcmp(buf, "29129"))
654                         return &platforms[WLA2_G54L];
655                 if (!strcmp(buf, "30189"))
656                         return &platforms[WHR_HP_G54];
657                 if (!strcmp(buf, "30182"))
658                         return &platforms[WHR_G54S];
659                 if (!strcmp(buf, "290441dd"))
660                         return &platforms[WHR2_A54G54];
661                 if (!strcmp(buf, "31120"))
662                         return &platforms[WZR_G300N];
663                 if (!strcmp(buf, "30083"))
664                         return &platforms[WZR_RS_G54];
665                 if (!strcmp(buf, "30103"))
666                         return &platforms[WZR_RS_G54HP];
667         }
668
669         if (buf || !strcmp(boardnum, "00")) {/* probably buffalo */
670                 if (!strncmp(boardtype, "bcm94710ap", 10))
671                         return &platforms[BUFFALO_UNKNOWN_4710];
672                 else
673                         return &platforms[BUFFALO_UNKNOWN];
674         }
675
676         if (!strcmp(getvar("CFEver"), "MotoWRv203") ||
677                 !strcmp(getvar("MOTO_BOARD_TYPE"), "WR_FEM1")) {
678
679                 return &platforms[WR850GV2V3];
680         }
681
682         if (!strcmp(boardnum, "44")) {  /* Trendware TEW-411BRP+ */
683                 return &platforms[TEW411BRPP];
684         }
685
686         if (!strncmp(boardnum, "04FN52", 6)) /* SimpleTech SimpleShare */
687                 return &platforms[STI_NAS];
688
689         /* not found */
690         return NULL;
691 }
692
693 static void register_buttons(struct button_t *b)
694 {
695         for (; b->name; b++)
696                 platform.button_mask |= b->gpio;
697
698         platform.button_mask &= ~gpiomask;
699
700         gpio_outen(platform.button_mask, 0);
701         gpio_control(platform.button_mask, 0);
702         platform.button_polarity = gpio_in() & platform.button_mask;
703         gpio_intpolarity(platform.button_mask, platform.button_polarity);
704         gpio_intmask(platform.button_mask, platform.button_mask);
705
706         gpio_set_irqenable(1, button_handler);
707 }
708
709 static void unregister_buttons(struct button_t *b)
710 {
711         gpio_intmask(platform.button_mask, 0);
712
713         gpio_set_irqenable(0, button_handler);
714 }
715
716 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
717 static void hotplug_button(struct work_struct *work)
718 {
719         struct event_t *event = container_of(work, struct event_t, wq);
720 #else
721 static void hotplug_button(struct event_t *event)
722 {
723 #endif
724 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
725         call_usermodehelper (event->argv[0], event->argv, event->envp, 1);
726 #else
727         call_usermodehelper (event->argv[0], event->argv, event->envp);
728 #endif
729         kfree(event);
730 }
731
732 static irqreturn_t button_handler(int irq, void *dev_id, struct pt_regs *regs)
733 {
734         struct button_t *b;
735         u32 in, changed;
736
737         in = gpio_in() & platform.button_mask;
738         gpio_intpolarity(platform.button_mask, in);
739         changed = platform.button_polarity ^ in;
740         platform.button_polarity = in;
741
742         changed &= ~gpio_outen(0, 0);
743
744         for (b = platform.buttons; b->name; b++) { 
745                 struct event_t *event;
746
747                 if (!(b->gpio & changed)) continue;
748
749                 b->pressed ^= 1;
750
751                 if ((event = (struct event_t *)kmalloc (sizeof(struct event_t), GFP_ATOMIC))) {
752                         int i;
753                         char *scratch = event->buf;
754
755                         i = 0;
756                         event->argv[i++] = hotplug_path;
757                         event->argv[i++] = "button";
758                         event->argv[i] = 0;
759
760                         i = 0;
761                         event->envp[i++] = "HOME=/";
762                         event->envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
763                         event->envp[i++] = scratch;
764                         scratch += sprintf (scratch, "ACTION=%s", b->pressed?"pressed":"released") + 1;
765                         event->envp[i++] = scratch;
766                         scratch += sprintf (scratch, "BUTTON=%s", b->name) + 1;
767                         event->envp[i++] = scratch;
768                         scratch += sprintf (scratch, "SEEN=%ld", (jiffies - b->seen)/HZ) + 1;
769                         event->envp[i] = 0;
770
771 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
772                         INIT_WORK(&event->wq, (void *)(void *)hotplug_button);
773 #else
774                         INIT_WORK(&event->wq, (void *)(void *)hotplug_button, (void *)event);
775 #endif
776                         schedule_work(&event->wq);
777                 }
778
779                 b->seen = jiffies;
780         }
781         return IRQ_HANDLED;
782 }
783
784 static void register_leds(struct led_t *l)
785 {
786         struct proc_dir_entry *p;
787         u32 mask = 0;
788         u32 val = 0;
789
790         leds = proc_mkdir("led", diag);
791         if (!leds) 
792                 return;
793
794         for(; l->name; l++) {
795                 if (l->gpio & gpiomask)
796                         continue;
797         
798                 if (l->gpio & GPIO_TYPE_EXTIF) {
799                         l->state = 0;
800                         set_led_extif(l);
801                 } else {
802                         mask |= l->gpio;
803                         val |= (l->polarity == NORMAL)?0:l->gpio;
804                 }
805
806                 if ((p = create_proc_entry(l->name, S_IRUSR, leds))) {
807                         l->proc.type = PROC_LED;
808                         l->proc.ptr = l;
809                         p->data = (void *) &l->proc;
810                         p->proc_fops = &diag_proc_fops;
811                 }
812         }
813
814         gpio_outen(mask, mask);
815         gpio_control(mask, 0);
816         gpio_out(mask, val);
817 }
818
819 static void unregister_leds(struct led_t *l)
820 {
821         for(; l->name; l++)
822                 remove_proc_entry(l->name, leds);
823
824         remove_proc_entry("led", diag);
825 }
826
827 static void set_led_extif(struct led_t *led)
828 {
829         gpio_set_extif(led->gpio, led->state);
830 }
831
832 static void led_flash(unsigned long dummy) {
833         struct led_t *l;
834         u32 mask = 0;
835         u8 extif_blink = 0;
836
837         for (l = platform.leds; l->name; l++) {
838                 if (l->flash) {
839                         if (l->gpio & GPIO_TYPE_EXTIF) {
840                                 extif_blink = 1;
841                                 l->state = !l->state;
842                                 set_led_extif(l);
843                         } else {
844                                 mask |= l->gpio;
845                         }
846                 }
847         }
848
849         mask &= ~gpiomask;
850         if (mask) {
851                 u32 val = ~gpio_in();
852
853                 gpio_outen(mask, mask);
854                 gpio_control(mask, 0);
855                 gpio_out(mask, val);
856         }
857         if (mask || extif_blink) {
858                 mod_timer(&led_timer, jiffies + FLASH_TIME);
859         }
860 }
861
862 static ssize_t diag_proc_read(struct file *file, char *buf, size_t count, loff_t *ppos)
863 {
864 #ifdef LINUX_2_4
865         struct inode *inode = file->f_dentry->d_inode;
866         struct proc_dir_entry *dent = inode->u.generic_ip;
867 #else
868         struct proc_dir_entry *dent = PDE(file->f_dentry->d_inode);
869 #endif
870         char *page;
871         int len = 0;
872         
873         if ((page = kmalloc(1024, GFP_KERNEL)) == NULL)
874                 return -ENOBUFS;
875         
876         if (dent->data != NULL) {
877                 struct prochandler_t *handler = (struct prochandler_t *) dent->data;
878                 switch (handler->type) {
879                         case PROC_LED: {
880                                 struct led_t * led = (struct led_t *) handler->ptr;
881                                 if (led->flash) {
882                                         len = sprintf(page, "f\n");
883                                 } else {
884                                         if (led->gpio & GPIO_TYPE_EXTIF) {
885                                                 len = sprintf(page, "%d\n", led->state);
886                                         } else {
887                                                 u32 in = (gpio_in() & led->gpio ? 1 : 0);
888                                                 u8 p = (led->polarity == NORMAL ? 0 : 1);
889                                                 len = sprintf(page, "%d\n", ((in ^ p) ? 1 : 0));
890                                         }
891                                 }
892                                 break;
893                         }
894                         case PROC_MODEL:
895                                 len = sprintf(page, "%s\n", platform.name);
896                                 break;
897                         case PROC_GPIOMASK:
898                                 len = sprintf(page, "0x%04x\n", gpiomask);
899                                 break;
900                 }
901         }
902         len += 1;
903
904         if (*ppos < len) {
905                 len = min_t(int, len - *ppos, count);
906                 if (copy_to_user(buf, (page + *ppos), len)) {
907                         kfree(page);
908                         return -EFAULT;
909                 }
910                 *ppos += len;
911         } else {
912                 len = 0;
913         }
914
915         return len;
916 }
917
918
919 static ssize_t diag_proc_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
920 {
921 #ifdef LINUX_2_4
922         struct inode *inode = file->f_dentry->d_inode;
923         struct proc_dir_entry *dent = inode->u.generic_ip;
924 #else
925         struct proc_dir_entry *dent = PDE(file->f_dentry->d_inode);
926 #endif
927         char *page;
928         int ret = -EINVAL;
929
930         if ((page = kmalloc(count + 1, GFP_KERNEL)) == NULL)
931                 return -ENOBUFS;
932
933         if (copy_from_user(page, buf, count)) {
934                 kfree(page);
935                 return -EINVAL;
936         }
937         page[count] = 0;
938         
939         if (dent->data != NULL) {
940                 struct prochandler_t *handler = (struct prochandler_t *) dent->data;
941                 switch (handler->type) {
942                         case PROC_LED: {
943                                 struct led_t *led = (struct led_t *) handler->ptr;
944                                 int p = (led->polarity == NORMAL ? 0 : 1);
945                                 
946                                 if (page[0] == 'f') {
947                                         led->flash = 1;
948                                         led_flash(0);
949                                 } else {
950                                         led->flash = 0;
951                                         if (led->gpio & GPIO_TYPE_EXTIF) {
952                                                 led->state = p ^ ((page[0] == '1') ? 1 : 0);
953                                                 set_led_extif(led);
954                                         } else {
955                                                 gpio_outen(led->gpio, led->gpio);
956                                                 gpio_control(led->gpio, 0);
957                                                 gpio_out(led->gpio, ((p ^ (page[0] == '1')) ? led->gpio : 0));
958                                         }
959                                 }
960                                 break;
961                         }
962                         case PROC_GPIOMASK:
963                                 gpiomask = simple_strtoul(page, NULL, 0);
964
965                                 if (platform.buttons) {
966                                         unregister_buttons(platform.buttons);
967                                         register_buttons(platform.buttons);
968                                 }
969
970                                 if (platform.leds) {
971                                         unregister_leds(platform.leds);
972                                         register_leds(platform.leds);
973                                 }
974                                 break;
975                 }
976                 ret = count;
977         }
978
979         kfree(page);
980         return ret;
981 }
982
983 static int __init diag_init(void)
984 {
985         static struct proc_dir_entry *p;
986         static struct platform_t *detected;
987
988         detected = platform_detect();
989         if (!detected) {
990                 printk(MODULE_NAME ": Router model not detected.\n");
991                 return -ENODEV;
992         }
993         memcpy(&platform, detected, sizeof(struct platform_t));
994
995         printk(MODULE_NAME ": Detected '%s'\n", platform.name);
996         if (platform.platform_init != NULL) {
997                 platform.platform_init();
998         }
999
1000         if (!(diag = proc_mkdir("diag", NULL))) {
1001                 printk(MODULE_NAME ": proc_mkdir on /proc/diag failed\n");
1002                 return -EINVAL;
1003         }
1004
1005         if ((p = create_proc_entry("model", S_IRUSR, diag))) {
1006                 p->data = (void *) &proc_model;
1007                 p->proc_fops = &diag_proc_fops;
1008         }
1009
1010         if ((p = create_proc_entry("gpiomask", S_IRUSR | S_IWUSR, diag))) {
1011                 p->data = (void *) &proc_gpiomask;
1012                 p->proc_fops = &diag_proc_fops;
1013         }
1014
1015         if (platform.buttons)
1016                 register_buttons(platform.buttons);
1017
1018         if (platform.leds)
1019                 register_leds(platform.leds);
1020
1021         return 0;
1022 }
1023
1024 static void __exit diag_exit(void)
1025 {
1026
1027         del_timer(&led_timer);
1028
1029         if (platform.buttons)
1030                 unregister_buttons(platform.buttons);
1031
1032         if (platform.leds)
1033                 unregister_leds(platform.leds);
1034
1035         remove_proc_entry("model", diag);
1036         remove_proc_entry("gpiomask", diag);
1037         remove_proc_entry("diag", NULL);
1038 }
1039
1040 module_init(diag_init);
1041 module_exit(diag_exit);
1042
1043 MODULE_AUTHOR("Mike Baker, Felix Fietkau / OpenWrt.org");
1044 MODULE_LICENSE("GPL");