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