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