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