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