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