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