1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36#include <linux/kernel.h>
37#include <linux/module.h>
38#include <linux/init.h>
39#include <linux/types.h>
40#include <linux/proc_fs.h>
41#include <acpi/acpi_drivers.h>
42#include <acpi/acpi_bus.h>
43#include <asm/uaccess.h>
44
45#define ASUS_ACPI_VERSION "0.29"
46
47#define PROC_ASUS "asus"
48#define PROC_MLED "mled"
49#define PROC_WLED "wled"
50#define PROC_TLED "tled"
51#define PROC_INFO "info"
52#define PROC_LCD "lcd"
53#define PROC_BRN "brn"
54#define PROC_DISP "disp"
55
56#define ACPI_HOTK_NAME "Asus Laptop ACPI Extras Driver"
57#define ACPI_HOTK_CLASS "hotkey"
58#define ACPI_HOTK_DEVICE_NAME "Hotkey"
59#define ACPI_HOTK_HID "ATK0100"
60
61
62
63
64#define BR_UP 0x10
65#define BR_DOWN 0x20
66
67
68
69
70#define MLED_ON 0x01
71#define WLED_ON 0x02
72#define TLED_ON 0x04
73
74MODULE_AUTHOR("Julien Lerouge, Karol Kozimor");
75MODULE_DESCRIPTION(ACPI_HOTK_NAME);
76MODULE_LICENSE("GPL");
77
78
79static uid_t asus_uid;
80static gid_t asus_gid;
81module_param(asus_uid, uint, 0);
82MODULE_PARM_DESC(uid, "UID for entries in /proc/acpi/asus.\n");
83module_param(asus_gid, uint, 0);
84MODULE_PARM_DESC(gid, "GID for entries in /proc/acpi/asus.\n");
85
86
87
88
89struct model_data {
90 char *name;
91 char *mt_mled;
92 char *mled_status;
93 char *mt_wled;
94 char *wled_status;
95 char *mt_tled;
96 char *tled_status;
97 char *mt_lcd_switch;
98 char *lcd_status;
99 char *brightness_up;
100 char *brightness_down;
101 char *brightness_set;
102 char *brightness_get;
103 char *brightness_status;
104 char *display_set;
105 char *display_get;
106};
107
108
109
110
111
112struct asus_hotk {
113 struct acpi_device *device;
114 acpi_handle handle;
115 char status;
116 struct model_data *methods;
117 u8 brightness;
118 enum {
119 A1x = 0,
120 A2x,
121 D1x,
122 L2D,
123 L3C,
124 L3D,
125 L3H,
126 L4R,
127 L5x,
128 L8L,
129 M1A,
130 M2E,
131 M6N,
132 M6R,
133 P30,
134 S1x,
135 S2x,
136 xxN,
137
138 END_MODEL
139 } model;
140 u16 event_count[128];
141};
142
143
144#define A1x_PREFIX "\\_SB.PCI0.ISA.EC0."
145#define L3C_PREFIX "\\_SB.PCI0.PX40.ECD0."
146#define M1A_PREFIX "\\_SB.PCI0.PX40.EC0."
147#define P30_PREFIX "\\_SB.PCI0.LPCB.EC0."
148#define S1x_PREFIX "\\_SB.PCI0.PX40."
149#define S2x_PREFIX A1x_PREFIX
150#define xxN_PREFIX "\\_SB.PCI0.SBRG.EC0."
151
152static struct model_data model_conf[END_MODEL] = {
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167 {
168 .name = "A1x",
169 .mt_mled = "MLED",
170 .mled_status = "\\MAIL",
171 .mt_lcd_switch = A1x_PREFIX "_Q10",
172 .lcd_status = "\\BKLI",
173 .brightness_up = A1x_PREFIX "_Q0E",
174 .brightness_down = A1x_PREFIX "_Q0F"
175 },
176
177 {
178 .name = "A2x",
179 .mt_mled = "MLED",
180 .mt_wled = "WLED",
181 .wled_status = "\\SG66",
182 .mt_lcd_switch = "\\Q10",
183 .lcd_status = "\\BAOF",
184 .brightness_set = "SPLV",
185 .brightness_get = "GPLV",
186 .display_set = "SDSP",
187 .display_get = "\\INFB"
188 },
189
190 {
191 .name = "D1x",
192 .mt_mled = "MLED",
193 .mt_lcd_switch = "\\Q0D",
194 .lcd_status = "\\GP11",
195 .brightness_up = "\\Q0C",
196 .brightness_down = "\\Q0B",
197 .brightness_status = "\\BLVL",
198 .display_set = "SDSP",
199 .display_get = "\\INFB"
200 },
201
202 {
203 .name = "L2D",
204 .mt_mled = "MLED",
205 .mled_status = "\\SGP6",
206 .mt_wled = "WLED",
207 .wled_status = "\\RCP3",
208 .mt_lcd_switch = "\\Q10",
209 .lcd_status = "\\SGP0",
210 .brightness_up = "\\Q0E",
211 .brightness_down = "\\Q0F",
212 .display_set = "SDSP",
213 .display_get = "\\INFB"
214 },
215
216 {
217 .name = "L3C",
218 .mt_mled = "MLED",
219 .mt_wled = "WLED",
220 .mt_lcd_switch = L3C_PREFIX "_Q10",
221 .lcd_status = "\\GL32",
222 .brightness_set = "SPLV",
223 .brightness_get = "GPLV",
224 .display_set = "SDSP",
225 .display_get = "\\_SB.PCI0.PCI1.VGAC.NMAP"
226 },
227
228 {
229 .name = "L3D",
230 .mt_mled = "MLED",
231 .mled_status = "\\MALD",
232 .mt_wled = "WLED",
233 .mt_lcd_switch = "\\Q10",
234 .lcd_status = "\\BKLG",
235 .brightness_set = "SPLV",
236 .brightness_get = "GPLV",
237 .display_set = "SDSP",
238 .display_get = "\\INFB"
239 },
240
241 {
242 .name = "L3H",
243 .mt_mled = "MLED",
244 .mt_wled = "WLED",
245 .mt_lcd_switch = "EHK",
246 .lcd_status = "\\_SB.PCI0.PM.PBC",
247 .brightness_set = "SPLV",
248 .brightness_get = "GPLV",
249 .display_set = "SDSP",
250 .display_get = "\\INFB"
251 },
252
253 {
254 .name = "L4R",
255 .mt_mled = "MLED",
256 .mt_wled = "WLED",
257 .wled_status = "\\_SB.PCI0.SBRG.SG13",
258 .mt_lcd_switch = xxN_PREFIX "_Q10",
259 .lcd_status = "\\_SB.PCI0.SBSM.SEO4",
260 .brightness_set = "SPLV",
261 .brightness_get = "GPLV",
262 .display_set = "SDSP",
263 .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"
264 },
265
266 {
267 .name = "L5x",
268 .mt_mled = "MLED",
269
270 .mt_tled = "TLED",
271 .mt_lcd_switch = "\\Q0D",
272 .lcd_status = "\\BAOF",
273 .brightness_set = "SPLV",
274 .brightness_get = "GPLV",
275 .display_set = "SDSP",
276 .display_get = "\\INFB"
277 },
278
279 {
280 .name = "L8L"
281
282 },
283
284 {
285 .name = "M1A",
286 .mt_mled = "MLED",
287 .mt_lcd_switch = M1A_PREFIX "Q10",
288 .lcd_status = "\\PNOF",
289 .brightness_up = M1A_PREFIX "Q0E",
290 .brightness_down = M1A_PREFIX "Q0F",
291 .brightness_status = "\\BRIT",
292 .display_set = "SDSP",
293 .display_get = "\\INFB"
294 },
295
296 {
297 .name = "M2E",
298 .mt_mled = "MLED",
299 .mt_wled = "WLED",
300 .mt_lcd_switch = "\\Q10",
301 .lcd_status = "\\GP06",
302 .brightness_set = "SPLV",
303 .brightness_get = "GPLV",
304 .display_set = "SDSP",
305 .display_get = "\\INFB"
306 },
307
308 {
309 .name = "M6N",
310 .mt_mled = "MLED",
311 .mt_wled = "WLED",
312 .wled_status = "\\_SB.PCI0.SBRG.SG13",
313 .mt_lcd_switch = xxN_PREFIX "_Q10",
314 .lcd_status = "\\_SB.BKLT",
315 .brightness_set = "SPLV",
316 .brightness_get = "GPLV",
317 .display_set = "SDSP",
318 .display_get = "\\SSTE"
319 },
320 {
321 .name = "M6R",
322 .mt_mled = "MLED",
323 .mt_wled = "WLED",
324 .mt_lcd_switch = xxN_PREFIX "_Q10",
325 .lcd_status = "\\_SB.PCI0.SBSM.SEO4",
326 .brightness_set = "SPLV",
327 .brightness_get = "GPLV",
328 .display_set = "SDSP",
329 .display_get = "\\SSTE"
330 },
331
332
333 {
334 .name = "P30",
335 .mt_wled = "WLED",
336 .mt_lcd_switch = P30_PREFIX "_Q0E",
337 .lcd_status = "\\BKLT",
338 .brightness_up = P30_PREFIX "_Q68",
339 .brightness_down = P30_PREFIX "_Q69",
340 .brightness_get = "GPLV",
341 .display_set = "SDSP",
342 .display_get = "\\DNXT"
343 },
344
345 {
346 .name = "S1x",
347 .mt_mled = "MLED",
348 .mled_status = "\\EMLE",
349 .mt_wled = "WLED",
350 .mt_lcd_switch = S1x_PREFIX "Q10" ,
351 .lcd_status = "\\PNOF",
352 .brightness_set = "SPLV",
353 .brightness_get = "GPLV"
354 },
355
356 {
357 .name = "S2x",
358 .mt_mled = "MLED",
359 .mled_status = "\\MAIL",
360 .mt_lcd_switch = S2x_PREFIX "_Q10",
361 .lcd_status = "\\BKLI",
362 .brightness_up = S2x_PREFIX "_Q0B",
363 .brightness_down = S2x_PREFIX "_Q0A"
364 },
365
366 {
367 .name = "xxN",
368 .mt_mled = "MLED",
369
370 .mt_lcd_switch = xxN_PREFIX "_Q10",
371 .lcd_status = "\\BKLT",
372 .brightness_set = "SPLV",
373 .brightness_get = "GPLV",
374 .display_set = "SDSP",
375 .display_get = "\\ADVG"
376 }
377};
378
379
380static struct proc_dir_entry *asus_proc_dir;
381
382
383
384
385
386
387static struct acpi_table_header *asus_info;
388
389
390static struct asus_hotk *hotk;
391
392
393
394
395static int asus_hotk_add(struct acpi_device *device);
396static int asus_hotk_remove(struct acpi_device *device, int type);
397static struct acpi_driver asus_hotk_driver = {
398 .name = ACPI_HOTK_NAME,
399 .class = ACPI_HOTK_CLASS,
400 .ids = ACPI_HOTK_HID,
401 .ops = {
402 .add = asus_hotk_add,
403 .remove = asus_hotk_remove,
404 },
405};
406
407
408
409
410
411
412
413
414static int write_acpi_int(acpi_handle handle, const char *method, int val,
415 struct acpi_buffer *output)
416{
417 struct acpi_object_list params;
418 union acpi_object in_obj;
419 acpi_status status;
420
421 params.count = 1;
422 params.pointer = &in_obj;
423 in_obj.type = ACPI_TYPE_INTEGER;
424 in_obj.integer.value = val;
425
426 status = acpi_evaluate_object(handle, (char *) method, ¶ms, output);
427 return (status == AE_OK);
428}
429
430
431static int read_acpi_int(acpi_handle handle, const char *method, int *val)
432{
433 struct acpi_buffer output;
434 union acpi_object out_obj;
435 acpi_status status;
436
437 output.length = sizeof(out_obj);
438 output.pointer = &out_obj;
439
440 status = acpi_evaluate_object(handle, (char *) method, NULL, &output);
441 *val = out_obj.integer.value;
442 return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER);
443}
444
445
446
447
448
449
450static int
451proc_read_info(char *page, char **start, off_t off, int count, int *eof,
452 void *data)
453{
454 int len = 0;
455 int temp;
456 char buf[16];
457
458
459
460
461
462 len += sprintf(page, ACPI_HOTK_NAME " " ASUS_ACPI_VERSION "\n");
463 len += sprintf(page + len, "Model reference : %s\n",
464 hotk->methods->name);
465
466
467
468
469
470
471 if (read_acpi_int(hotk->handle, "SFUN", &temp))
472 len += sprintf(page + len, "SFUN value : 0x%04x\n", temp);
473
474
475
476
477
478
479
480 if (read_acpi_int(hotk->handle, "ASYM", &temp))
481 len += sprintf(page + len, "ASYM value : 0x%04x\n", temp);
482 if (asus_info) {
483 snprintf(buf, 16, "%d", asus_info->length);
484 len += sprintf(page + len, "DSDT length : %s\n", buf);
485 snprintf(buf, 16, "%d", asus_info->checksum);
486 len += sprintf(page + len, "DSDT checksum : %s\n", buf);
487 snprintf(buf, 16, "%d", asus_info->revision);
488 len += sprintf(page + len, "DSDT revision : %s\n", buf);
489 snprintf(buf, 7, "%s", asus_info->oem_id);
490 len += sprintf(page + len, "OEM id : %s\n", buf);
491 snprintf(buf, 9, "%s", asus_info->oem_table_id);
492 len += sprintf(page + len, "OEM table id : %s\n", buf);
493 snprintf(buf, 16, "%x", asus_info->oem_revision);
494 len += sprintf(page + len, "OEM revision : 0x%s\n", buf);
495 snprintf(buf, 5, "%s", asus_info->asl_compiler_id);
496 len += sprintf(page + len, "ASL comp vendor id : %s\n", buf);
497 snprintf(buf, 16, "%x", asus_info->asl_compiler_revision);
498 len += sprintf(page + len, "ASL comp revision : 0x%s\n", buf);
499 }
500
501 return len;
502}
503
504
505
506
507
508
509
510
511
512
513static int
514read_led(const char *ledname, int ledmask)
515{
516 if (ledname) {
517 int led_status;
518
519 if (read_acpi_int(NULL, ledname, &led_status))
520 return led_status;
521 else
522 printk(KERN_WARNING "Asus ACPI: Error reading LED "
523 "status\n");
524 }
525 return (hotk->status & ledmask) ? 1 : 0;
526}
527
528static int parse_arg(const char __user *buf, unsigned long count, int *val)
529{
530 char s[32];
531 if (!count)
532 return 0;
533 if (count > 31)
534 return -EINVAL;
535 if (copy_from_user(s, buf, count))
536 return -EFAULT;
537 s[count] = 0;
538 if (sscanf(s, "%i", val) != 1)
539 return -EINVAL;
540 return count;
541}
542
543
544static int
545write_led(const char __user *buffer, unsigned long count,
546 char *ledname, int ledmask, int invert)
547{
548 int value;
549 int led_out = 0;
550
551 count = parse_arg(buffer, count, &value);
552 if (count > 0)
553 led_out = value ? 1 : 0;
554
555 hotk->status =
556 (led_out) ? (hotk->status | ledmask) : (hotk->status & ~ledmask);
557
558 if (invert)
559 led_out = !led_out & 0x1;
560
561 if (!write_acpi_int(hotk->handle, ledname, led_out, NULL))
562 printk(KERN_WARNING "Asus ACPI: LED (%s) write failed\n", ledname);
563
564 return count;
565}
566
567
568
569
570
571static int
572proc_read_mled(char *page, char **start, off_t off, int count, int *eof,
573 void *data)
574{
575 return sprintf(page, "%d\n", read_led(hotk->methods->mled_status, MLED_ON));
576}
577
578
579static int
580proc_write_mled(struct file *file, const char __user *buffer,
581 unsigned long count, void *data)
582{
583 return write_led(buffer, count, hotk->methods->mt_mled, MLED_ON, 1);
584}
585
586
587
588
589static int
590proc_read_wled(char *page, char **start, off_t off, int count, int *eof,
591 void *data)
592{
593 return sprintf(page, "%d\n", read_led(hotk->methods->wled_status, WLED_ON));
594}
595
596static int
597proc_write_wled(struct file *file, const char __user *buffer,
598 unsigned long count, void *data)
599{
600 return write_led(buffer, count, hotk->methods->mt_wled, WLED_ON, 0);
601}
602
603
604
605
606static int
607proc_read_tled(char *page, char **start, off_t off, int count, int *eof,
608 void *data)
609{
610 return sprintf(page, "%d\n", read_led(hotk->methods->tled_status, TLED_ON));
611}
612
613static int
614proc_write_tled(struct file *file, const char __user *buffer,
615 unsigned long count, void *data)
616{
617 return write_led(buffer, count, hotk->methods->mt_tled, TLED_ON, 0);
618}
619
620
621static int get_lcd_state(void)
622{
623 int lcd = 0;
624
625 if (hotk->model != L3H) {
626
627 if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd))
628 printk(KERN_WARNING "Asus ACPI: Error reading LCD status\n");
629
630 if (hotk->model == L2D)
631 lcd = ~lcd;
632 } else {
633 acpi_status status = 0;
634 struct acpi_object_list input;
635 union acpi_object mt_params[2];
636 struct acpi_buffer output;
637 union acpi_object out_obj;
638
639 input.count = 2;
640 input.pointer = mt_params;
641
642
643 mt_params[0].type = ACPI_TYPE_INTEGER;
644 mt_params[0].integer.value = 0x02;
645 mt_params[1].type = ACPI_TYPE_INTEGER;
646 mt_params[1].integer.value = 0x02;
647
648 output.length = sizeof(out_obj);
649 output.pointer = &out_obj;
650
651 status = acpi_evaluate_object(NULL, hotk->methods->lcd_status, &input, &output);
652 if (status != AE_OK)
653 return -1;
654 if (out_obj.type == ACPI_TYPE_INTEGER)
655
656 lcd = out_obj.integer.value >> 8;
657 }
658
659 return (lcd & 1);
660}
661
662static int set_lcd_state(int value)
663{
664 int lcd = 0;
665 acpi_status status = 0;
666
667 lcd = value ? 1 : 0;
668 if (lcd != get_lcd_state()) {
669
670 if (hotk->model != L3H) {
671 status =
672 acpi_evaluate_object(NULL, hotk->methods->mt_lcd_switch,
673 NULL, NULL);
674 } else {
675 if (!write_acpi_int(hotk->handle, hotk->methods->mt_lcd_switch, 0x07, NULL))
676 status = AE_ERROR;
677
678
679 }
680 if (ACPI_FAILURE(status))
681 printk(KERN_WARNING "Asus ACPI: Error switching LCD\n");
682 }
683 return 0;
684
685}
686
687static int
688proc_read_lcd(char *page, char **start, off_t off, int count, int *eof,
689 void *data)
690{
691 return sprintf(page, "%d\n", get_lcd_state());
692}
693
694
695static int
696proc_write_lcd(struct file *file, const char __user *buffer,
697 unsigned long count, void *data)
698{
699 int value;
700
701 count = parse_arg(buffer, count, &value);
702 if (count > 0)
703 set_lcd_state(value);
704 return count;
705}
706
707
708static int read_brightness(void)
709{
710 int value;
711
712 if(hotk->methods->brightness_get) {
713 if (!read_acpi_int(hotk->handle, hotk->methods->brightness_get,
714 &value))
715 printk(KERN_WARNING "Asus ACPI: Error reading brightness\n");
716 } else if (hotk->methods->brightness_status) {
717 if (!read_acpi_int(NULL, hotk->methods->brightness_status,
718 &value))
719 printk(KERN_WARNING "Asus ACPI: Error reading brightness\n");
720 } else
721 value = hotk->brightness;
722 return value;
723}
724
725
726
727
728static void set_brightness(int value)
729{
730 acpi_status status = 0;
731
732
733 if(hotk->methods->brightness_set) {
734 if (!write_acpi_int(hotk->handle, hotk->methods->brightness_set,
735 value, NULL))
736 printk(KERN_WARNING "Asus ACPI: Error changing brightness\n");
737 return;
738 }
739
740
741 value -= read_brightness();
742 while (value != 0) {
743 status = acpi_evaluate_object(NULL, (value > 0) ?
744 hotk->methods->brightness_up :
745 hotk->methods->brightness_down,
746 NULL, NULL);
747 (value > 0) ? value-- : value++;
748 if (ACPI_FAILURE(status))
749 printk(KERN_WARNING "Asus ACPI: Error changing brightness\n");
750 }
751 return;
752}
753
754static int
755proc_read_brn(char *page, char **start, off_t off, int count, int *eof,
756 void *data)
757{
758 return sprintf(page, "%d\n", read_brightness());
759}
760
761static int
762proc_write_brn(struct file *file, const char __user *buffer,
763 unsigned long count, void *data)
764{
765 int value;
766
767 count = parse_arg(buffer, count, &value);
768 if (count > 0) {
769 value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
770
771 set_brightness(value);
772 } else if (count < 0) {
773 printk(KERN_WARNING "Asus ACPI: Error reading user input\n");
774 }
775
776 return count;
777}
778
779static void set_display(int value)
780{
781
782 if (!write_acpi_int(hotk->handle, hotk->methods->display_set,
783 value, NULL))
784 printk(KERN_WARNING "Asus ACPI: Error setting display\n");
785 return;
786}
787
788
789
790
791
792static int
793proc_read_disp(char *page, char **start, off_t off, int count, int *eof,
794 void *data)
795{
796 int value = 0;
797
798 if (!read_acpi_int(hotk->handle, hotk->methods->display_get, &value))
799 printk(KERN_WARNING "Asus ACPI: Error reading display status\n");
800 value &= 0x07;
801 return sprintf(page, "%d\n", value);
802}
803
804
805
806
807
808
809
810static int
811proc_write_disp(struct file *file, const char __user *buffer,
812 unsigned long count, void *data)
813{
814 int value;
815
816 count = parse_arg(buffer, count, &value);
817 if (count > 0)
818 set_display(value);
819 else if (count < 0)
820 printk(KERN_WARNING "Asus ACPI: Error reading user input\n");
821
822 return count;
823}
824
825
826typedef int (proc_readfunc)(char *page, char **start, off_t off, int count,
827 int *eof, void *data);
828typedef int (proc_writefunc)(struct file *file, const char __user *buffer,
829 unsigned long count, void *data);
830
831static int
832__init asus_proc_add(char *name, proc_writefunc *writefunc,
833 proc_readfunc *readfunc, mode_t mode,
834 struct acpi_device *device)
835{
836 struct proc_dir_entry *proc = create_proc_entry(name, mode, acpi_device_dir(device));
837 if(!proc) {
838 printk(KERN_WARNING " Unable to create %s fs entry\n", name);
839 return -1;
840 }
841 proc->write_proc = writefunc;
842 proc->read_proc = readfunc;
843 proc->data = acpi_driver_data(device);
844 proc->owner = THIS_MODULE;
845 proc->uid = asus_uid;
846 proc->gid = asus_gid;
847 return 0;
848}
849
850static int __init asus_hotk_add_fs(struct acpi_device *device)
851{
852 struct proc_dir_entry *proc;
853 mode_t mode;
854
855
856
857
858
859
860
861 if ((asus_uid == 0) && (asus_gid == 0)){
862 mode = S_IFREG | S_IRUGO | S_IWUGO;
863 } else {
864 mode = S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP;
865 }
866
867 acpi_device_dir(device) = asus_proc_dir;
868 if (!acpi_device_dir(device))
869 return -ENODEV;
870
871 proc = create_proc_entry(PROC_INFO, mode, acpi_device_dir(device));
872 if (proc) {
873 proc->read_proc = proc_read_info;
874 proc->data = acpi_driver_data(device);
875 proc->owner = THIS_MODULE;
876 proc->uid = asus_uid;
877 proc->gid = asus_gid;
878 } else {
879 printk(KERN_WARNING " Unable to create " PROC_INFO
880 " fs entry\n");
881 }
882
883 if (hotk->methods->mt_wled) {
884 asus_proc_add(PROC_WLED, &proc_write_wled, &proc_read_wled, mode, device);
885 }
886
887 if (hotk->methods->mt_mled) {
888 asus_proc_add(PROC_MLED, &proc_write_mled, &proc_read_mled, mode, device);
889 }
890
891 if (hotk->methods->mt_tled) {
892 asus_proc_add(PROC_TLED, &proc_write_tled, &proc_read_tled, mode, device);
893 }
894
895
896
897
898
899 if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status) {
900 asus_proc_add(PROC_LCD, &proc_write_lcd, &proc_read_lcd, mode, device);
901 }
902
903 if ((hotk->methods->brightness_up && hotk->methods->brightness_down) ||
904 (hotk->methods->brightness_get && hotk->methods->brightness_set)) {
905 asus_proc_add(PROC_BRN, &proc_write_brn, &proc_read_brn, mode, device);
906 }
907
908 if (hotk->methods->display_set) {
909 asus_proc_add(PROC_DISP, &proc_write_disp, &proc_read_disp, mode, device);
910 }
911
912 return 0;
913}
914
915static int asus_hotk_remove_fs(struct acpi_device* device)
916{
917 if(acpi_device_dir(device)) {
918 remove_proc_entry(PROC_INFO,acpi_device_dir(device));
919 if (hotk->methods->mt_wled)
920 remove_proc_entry(PROC_WLED,acpi_device_dir(device));
921 if (hotk->methods->mt_mled)
922 remove_proc_entry(PROC_MLED,acpi_device_dir(device));
923 if (hotk->methods->mt_tled)
924 remove_proc_entry(PROC_TLED,acpi_device_dir(device));
925 if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status)
926 remove_proc_entry(PROC_LCD, acpi_device_dir(device));
927 if ((hotk->methods->brightness_up && hotk->methods->brightness_down) ||
928 (hotk->methods->brightness_get && hotk->methods->brightness_set))
929 remove_proc_entry(PROC_BRN, acpi_device_dir(device));
930 if (hotk->methods->display_set)
931 remove_proc_entry(PROC_DISP, acpi_device_dir(device));
932 }
933 return 0;
934}
935
936
937static void asus_hotk_notify(acpi_handle handle, u32 event, void *data)
938{
939
940 if (!hotk)
941 return;
942
943 if ((event & ~((u32) BR_UP)) < 16) {
944 hotk->brightness = (event & ~((u32) BR_UP));
945 } else if ((event & ~((u32) BR_DOWN)) < 16 ) {
946 hotk->brightness = (event & ~((u32) BR_DOWN));
947 }
948
949 acpi_bus_generate_event(hotk->device, event,
950 hotk->event_count[event % 128]++);
951
952 return;
953}
954
955
956
957
958
959static int __init asus_hotk_get_info(void)
960{
961 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
962 struct acpi_buffer dsdt = { ACPI_ALLOCATE_BUFFER, NULL };
963 union acpi_object *model = NULL;
964 int bsts_result;
965 acpi_status status;
966
967
968
969
970
971
972
973
974
975 status = acpi_get_table(ACPI_TABLE_DSDT, 1, &dsdt);
976 if (ACPI_FAILURE(status))
977 printk(KERN_WARNING " Couldn't get the DSDT table header\n");
978 else
979 asus_info = (struct acpi_table_header *) dsdt.pointer;
980
981
982 if (!write_acpi_int(hotk->handle, "INIT", 0, &buffer)) {
983 printk(KERN_ERR " Hotkey initialization failed\n");
984 return -ENODEV;
985 }
986
987
988 if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result))
989 printk(KERN_WARNING " Error calling BSTS\n");
990 else if (bsts_result)
991 printk(KERN_NOTICE " BSTS called, 0x%02x returned\n", bsts_result);
992
993
994
995 if (buffer.pointer == NULL) {
996 if (asus_info &&
997 strncmp(asus_info->oem_table_id, "ODEM", 4) == 0) {
998 hotk->model = P30;
999 printk(KERN_NOTICE " Samsung P30 detected, supported\n");
1000 } else {
1001 hotk->model = M2E;
1002 printk(KERN_WARNING " no string returned by INIT\n");
1003 printk(KERN_WARNING " trying default values, supply "
1004 "the developers with your DSDT\n");
1005 }
1006 hotk->methods = &model_conf[hotk->model];
1007 return AE_OK;
1008 }
1009
1010 model = (union acpi_object *) buffer.pointer;
1011 if (model->type == ACPI_TYPE_STRING) {
1012 printk(KERN_NOTICE " %s model detected, ", model->string.pointer);
1013 }
1014
1015 hotk->model = END_MODEL;
1016 if (strncmp(model->string.pointer, "L3D", 3) == 0)
1017 hotk->model = L3D;
1018 else if (strncmp(model->string.pointer, "L3H", 3) == 0 ||
1019 strncmp(model->string.pointer, "L2E", 3) == 0)
1020 hotk->model = L3H;
1021 else if (strncmp(model->string.pointer, "L3", 2) == 0 ||
1022 strncmp(model->string.pointer, "L2B", 3) == 0)
1023 hotk->model = L3C;
1024 else if (strncmp(model->string.pointer, "L8L", 3) == 0)
1025 hotk->model = L8L;
1026 else if (strncmp(model->string.pointer, "L4R", 3) == 0)
1027 hotk->model = L4R;
1028 else if (strncmp(model->string.pointer, "M6N", 3) == 0)
1029 hotk->model = M6N;
1030 else if (strncmp(model->string.pointer, "M6R", 3) == 0)
1031 hotk->model = M6R;
1032 else if (strncmp(model->string.pointer, "M2N", 3) == 0 ||
1033 strncmp(model->string.pointer, "M3N", 3) == 0 ||
1034 strncmp(model->string.pointer, "M5N", 3) == 0 ||
1035 strncmp(model->string.pointer, "M6N", 3) == 0 ||
1036 strncmp(model->string.pointer, "S1N", 3) == 0 ||
1037 strncmp(model->string.pointer, "S5N", 3) == 0 ||
1038 strncmp(model->string.pointer, "W1N", 3) == 0)
1039 hotk->model = xxN;
1040 else if (strncmp(model->string.pointer, "M1", 2) == 0)
1041 hotk->model = M1A;
1042 else if (strncmp(model->string.pointer, "M2", 2) == 0 ||
1043 strncmp(model->string.pointer, "L4E", 3) == 0)
1044 hotk->model = M2E;
1045 else if (strncmp(model->string.pointer, "L2", 2) == 0)
1046 hotk->model = L2D;
1047 else if (strncmp(model->string.pointer, "L8", 2) == 0)
1048 hotk->model = S1x;
1049 else if (strncmp(model->string.pointer, "D1", 2) == 0)
1050 hotk->model = D1x;
1051 else if (strncmp(model->string.pointer, "A1", 2) == 0)
1052 hotk->model = A1x;
1053 else if (strncmp(model->string.pointer, "A2", 2) == 0)
1054 hotk->model = A2x;
1055 else if (strncmp(model->string.pointer, "J1", 2) == 0)
1056 hotk->model = S2x;
1057 else if (strncmp(model->string.pointer, "L5", 2) == 0)
1058 hotk->model = L5x;
1059
1060 if (hotk->model == END_MODEL) {
1061 printk("unsupported, trying default values, supply the "
1062 "developers with your DSDT\n");
1063 hotk->model = M2E;
1064 } else {
1065 printk("supported\n");
1066 }
1067
1068 hotk->methods = &model_conf[hotk->model];
1069
1070
1071 if (strncmp(model->string.pointer, "L2B", 3) == 0)
1072 hotk->methods->lcd_status = NULL;
1073
1074
1075 else if (strncmp(model->string.pointer, "S5N", 3) == 0 ||
1076 strncmp(model->string.pointer, "M5N", 3) == 0)
1077 hotk->methods->mt_mled = NULL;
1078
1079 else if (strncmp(model->string.pointer, "M2N", 3) == 0 ||
1080 strncmp(model->string.pointer, "W1N", 3) == 0)
1081 hotk->methods->mt_wled = "WLED";
1082
1083 else if (asus_info) {
1084 if (strncmp(asus_info->oem_table_id, "L1", 2) == 0)
1085 hotk->methods->mled_status = NULL;
1086
1087 }
1088
1089 acpi_os_free(model);
1090
1091 return AE_OK;
1092}
1093
1094
1095static int __init asus_hotk_check(void)
1096{
1097 int result = 0;
1098
1099 result = acpi_bus_get_status(hotk->device);
1100 if (result)
1101 return result;
1102
1103 if (hotk->device->status.present) {
1104 result = asus_hotk_get_info();
1105 } else {
1106 printk(KERN_ERR " Hotkey device not present, aborting\n");
1107 return -EINVAL;
1108 }
1109
1110 return result;
1111}
1112
1113
1114static int __init asus_hotk_add(struct acpi_device *device)
1115{
1116 acpi_status status = AE_OK;
1117 int result;
1118
1119 if (!device)
1120 return -EINVAL;
1121
1122 printk(KERN_NOTICE "Asus Laptop ACPI Extras version %s\n",
1123 ASUS_ACPI_VERSION);
1124
1125 hotk =
1126 (struct asus_hotk *) kmalloc(sizeof(struct asus_hotk), GFP_KERNEL);
1127 if (!hotk)
1128 return -ENOMEM;
1129 memset(hotk, 0, sizeof(struct asus_hotk));
1130
1131 hotk->handle = device->handle;
1132 strcpy(acpi_device_name(device), ACPI_HOTK_DEVICE_NAME);
1133 strcpy(acpi_device_class(device), ACPI_HOTK_CLASS);
1134 acpi_driver_data(device) = hotk;
1135 hotk->device = device;
1136
1137
1138 result = asus_hotk_check();
1139 if (result)
1140 goto end;
1141
1142 result = asus_hotk_add_fs(device);
1143 if (result)
1144 goto end;
1145
1146
1147
1148
1149
1150 status = acpi_install_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY,
1151 asus_hotk_notify, hotk);
1152 if (ACPI_FAILURE(status))
1153 printk(KERN_ERR " Error installing notify handler\n");
1154
1155
1156 if ((!hotk->methods->brightness_get) && (!hotk->methods->brightness_status) &&
1157 (hotk->methods->brightness_up && hotk->methods->brightness_down)) {
1158 status = acpi_evaluate_object(NULL, hotk->methods->brightness_down,
1159 NULL, NULL);
1160 if (ACPI_FAILURE(status))
1161 printk(KERN_WARNING " Error changing brightness\n");
1162 else {
1163 status = acpi_evaluate_object(NULL, hotk->methods->brightness_up,
1164 NULL, NULL);
1165 if (ACPI_FAILURE(status))
1166 printk(KERN_WARNING " Strange, error changing"
1167 " brightness\n");
1168 }
1169 }
1170
1171 end:
1172 if (result) {
1173 kfree(hotk);
1174 }
1175
1176 return result;
1177}
1178
1179
1180static int asus_hotk_remove(struct acpi_device *device, int type)
1181{
1182 acpi_status status = 0;
1183
1184 if (!device || !acpi_driver_data(device))
1185 return -EINVAL;
1186
1187 status = acpi_remove_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY,
1188 asus_hotk_notify);
1189 if (ACPI_FAILURE(status))
1190 printk(KERN_ERR "Asus ACPI: Error removing notify handler\n");
1191
1192 asus_hotk_remove_fs(device);
1193
1194 kfree(hotk);
1195
1196 return 0;
1197}
1198
1199
1200static int __init asus_acpi_init(void)
1201{
1202 int result;
1203
1204 if (acpi_disabled)
1205 return -ENODEV;
1206
1207 asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir);
1208 if (!asus_proc_dir) {
1209 printk(KERN_ERR "Asus ACPI: Unable to create /proc entry\n");
1210 return -ENODEV;
1211 }
1212 asus_proc_dir->owner = THIS_MODULE;
1213
1214 result = acpi_bus_register_driver(&asus_hotk_driver);
1215 if (result < 1) {
1216 acpi_bus_unregister_driver(&asus_hotk_driver);
1217 remove_proc_entry(PROC_ASUS, acpi_root_dir);
1218 return -ENODEV;
1219 }
1220
1221 return 0;
1222}
1223
1224
1225static void __exit asus_acpi_exit(void)
1226{
1227 acpi_bus_unregister_driver(&asus_hotk_driver);
1228 remove_proc_entry(PROC_ASUS, acpi_root_dir);
1229
1230 acpi_os_free(asus_info);
1231
1232 return;
1233}
1234
1235module_init(asus_acpi_init);
1236module_exit(asus_acpi_exit);
1237