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