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