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