diff options
Diffstat (limited to 'drivers/platform/x86/toshiba_acpi.c')
-rw-r--r-- | drivers/platform/x86/toshiba_acpi.c | 475 |
1 files changed, 352 insertions, 123 deletions
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 51c0a8bee414..37aa14798551 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c | |||
@@ -42,9 +42,12 @@ | |||
42 | #include <linux/init.h> | 42 | #include <linux/init.h> |
43 | #include <linux/types.h> | 43 | #include <linux/types.h> |
44 | #include <linux/proc_fs.h> | 44 | #include <linux/proc_fs.h> |
45 | #include <linux/seq_file.h> | ||
45 | #include <linux/backlight.h> | 46 | #include <linux/backlight.h> |
46 | #include <linux/platform_device.h> | 47 | #include <linux/platform_device.h> |
47 | #include <linux/rfkill.h> | 48 | #include <linux/rfkill.h> |
49 | #include <linux/input.h> | ||
50 | #include <linux/slab.h> | ||
48 | 51 | ||
49 | #include <asm/uaccess.h> | 52 | #include <asm/uaccess.h> |
50 | 53 | ||
@@ -61,9 +64,10 @@ MODULE_LICENSE("GPL"); | |||
61 | 64 | ||
62 | /* Toshiba ACPI method paths */ | 65 | /* Toshiba ACPI method paths */ |
63 | #define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM" | 66 | #define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM" |
64 | #define METHOD_HCI_1 "\\_SB_.VALD.GHCI" | 67 | #define TOSH_INTERFACE_1 "\\_SB_.VALD" |
65 | #define METHOD_HCI_2 "\\_SB_.VALZ.GHCI" | 68 | #define TOSH_INTERFACE_2 "\\_SB_.VALZ" |
66 | #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" | 69 | #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" |
70 | #define GHCI_METHOD ".GHCI" | ||
67 | 71 | ||
68 | /* Toshiba HCI interface definitions | 72 | /* Toshiba HCI interface definitions |
69 | * | 73 | * |
@@ -115,6 +119,36 @@ static const struct acpi_device_id toshiba_device_ids[] = { | |||
115 | }; | 119 | }; |
116 | MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); | 120 | MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); |
117 | 121 | ||
122 | struct key_entry { | ||
123 | char type; | ||
124 | u16 code; | ||
125 | u16 keycode; | ||
126 | }; | ||
127 | |||
128 | enum {KE_KEY, KE_END}; | ||
129 | |||
130 | static struct key_entry toshiba_acpi_keymap[] = { | ||
131 | {KE_KEY, 0x101, KEY_MUTE}, | ||
132 | {KE_KEY, 0x13b, KEY_COFFEE}, | ||
133 | {KE_KEY, 0x13c, KEY_BATTERY}, | ||
134 | {KE_KEY, 0x13d, KEY_SLEEP}, | ||
135 | {KE_KEY, 0x13e, KEY_SUSPEND}, | ||
136 | {KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE}, | ||
137 | {KE_KEY, 0x140, KEY_BRIGHTNESSDOWN}, | ||
138 | {KE_KEY, 0x141, KEY_BRIGHTNESSUP}, | ||
139 | {KE_KEY, 0x142, KEY_WLAN}, | ||
140 | {KE_KEY, 0x143, KEY_PROG1}, | ||
141 | {KE_KEY, 0xb05, KEY_PROG2}, | ||
142 | {KE_KEY, 0xb06, KEY_WWW}, | ||
143 | {KE_KEY, 0xb07, KEY_MAIL}, | ||
144 | {KE_KEY, 0xb30, KEY_STOP}, | ||
145 | {KE_KEY, 0xb31, KEY_PREVIOUSSONG}, | ||
146 | {KE_KEY, 0xb32, KEY_NEXTSONG}, | ||
147 | {KE_KEY, 0xb33, KEY_PLAYPAUSE}, | ||
148 | {KE_KEY, 0xb5a, KEY_MEDIA}, | ||
149 | {KE_END, 0, 0}, | ||
150 | }; | ||
151 | |||
118 | /* utility | 152 | /* utility |
119 | */ | 153 | */ |
120 | 154 | ||
@@ -250,6 +284,8 @@ static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result) | |||
250 | struct toshiba_acpi_dev { | 284 | struct toshiba_acpi_dev { |
251 | struct platform_device *p_dev; | 285 | struct platform_device *p_dev; |
252 | struct rfkill *bt_rfk; | 286 | struct rfkill *bt_rfk; |
287 | struct input_dev *hotkey_dev; | ||
288 | acpi_handle handle; | ||
253 | 289 | ||
254 | const char *bt_name; | 290 | const char *bt_name; |
255 | 291 | ||
@@ -357,63 +393,6 @@ static int force_fan; | |||
357 | static int last_key_event; | 393 | static int last_key_event; |
358 | static int key_event_valid; | 394 | static int key_event_valid; |
359 | 395 | ||
360 | typedef struct _ProcItem { | ||
361 | const char *name; | ||
362 | char *(*read_func) (char *); | ||
363 | unsigned long (*write_func) (const char *, unsigned long); | ||
364 | } ProcItem; | ||
365 | |||
366 | /* proc file handlers | ||
367 | */ | ||
368 | |||
369 | static int | ||
370 | dispatch_read(char *page, char **start, off_t off, int count, int *eof, | ||
371 | ProcItem * item) | ||
372 | { | ||
373 | char *p = page; | ||
374 | int len; | ||
375 | |||
376 | if (off == 0) | ||
377 | p = item->read_func(p); | ||
378 | |||
379 | /* ISSUE: I don't understand this code */ | ||
380 | len = (p - page); | ||
381 | if (len <= off + count) | ||
382 | *eof = 1; | ||
383 | *start = page + off; | ||
384 | len -= off; | ||
385 | if (len > count) | ||
386 | len = count; | ||
387 | if (len < 0) | ||
388 | len = 0; | ||
389 | return len; | ||
390 | } | ||
391 | |||
392 | static int | ||
393 | dispatch_write(struct file *file, const char __user * buffer, | ||
394 | unsigned long count, ProcItem * item) | ||
395 | { | ||
396 | int result; | ||
397 | char *tmp_buffer; | ||
398 | |||
399 | /* Arg buffer points to userspace memory, which can't be accessed | ||
400 | * directly. Since we're making a copy, zero-terminate the | ||
401 | * destination so that sscanf can be used on it safely. | ||
402 | */ | ||
403 | tmp_buffer = kmalloc(count + 1, GFP_KERNEL); | ||
404 | if (!tmp_buffer) | ||
405 | return -ENOMEM; | ||
406 | |||
407 | if (copy_from_user(tmp_buffer, buffer, count)) { | ||
408 | result = -EFAULT; | ||
409 | } else { | ||
410 | tmp_buffer[count] = 0; | ||
411 | result = item->write_func(tmp_buffer, count); | ||
412 | } | ||
413 | kfree(tmp_buffer); | ||
414 | return result; | ||
415 | } | ||
416 | |||
417 | static int get_lcd(struct backlight_device *bd) | 396 | static int get_lcd(struct backlight_device *bd) |
418 | { | 397 | { |
419 | u32 hci_result; | 398 | u32 hci_result; |
@@ -426,19 +405,24 @@ static int get_lcd(struct backlight_device *bd) | |||
426 | return -EFAULT; | 405 | return -EFAULT; |
427 | } | 406 | } |
428 | 407 | ||
429 | static char *read_lcd(char *p) | 408 | static int lcd_proc_show(struct seq_file *m, void *v) |
430 | { | 409 | { |
431 | int value = get_lcd(NULL); | 410 | int value = get_lcd(NULL); |
432 | 411 | ||
433 | if (value >= 0) { | 412 | if (value >= 0) { |
434 | p += sprintf(p, "brightness: %d\n", value); | 413 | seq_printf(m, "brightness: %d\n", value); |
435 | p += sprintf(p, "brightness_levels: %d\n", | 414 | seq_printf(m, "brightness_levels: %d\n", |
436 | HCI_LCD_BRIGHTNESS_LEVELS); | 415 | HCI_LCD_BRIGHTNESS_LEVELS); |
437 | } else { | 416 | } else { |
438 | printk(MY_ERR "Error reading LCD brightness\n"); | 417 | printk(MY_ERR "Error reading LCD brightness\n"); |
439 | } | 418 | } |
440 | 419 | ||
441 | return p; | 420 | return 0; |
421 | } | ||
422 | |||
423 | static int lcd_proc_open(struct inode *inode, struct file *file) | ||
424 | { | ||
425 | return single_open(file, lcd_proc_show, NULL); | ||
442 | } | 426 | } |
443 | 427 | ||
444 | static int set_lcd(int value) | 428 | static int set_lcd(int value) |
@@ -458,12 +442,20 @@ static int set_lcd_status(struct backlight_device *bd) | |||
458 | return set_lcd(bd->props.brightness); | 442 | return set_lcd(bd->props.brightness); |
459 | } | 443 | } |
460 | 444 | ||
461 | static unsigned long write_lcd(const char *buffer, unsigned long count) | 445 | static ssize_t lcd_proc_write(struct file *file, const char __user *buf, |
446 | size_t count, loff_t *pos) | ||
462 | { | 447 | { |
448 | char cmd[42]; | ||
449 | size_t len; | ||
463 | int value; | 450 | int value; |
464 | int ret; | 451 | int ret; |
465 | 452 | ||
466 | if (sscanf(buffer, " brightness : %i", &value) == 1 && | 453 | len = min(count, sizeof(cmd) - 1); |
454 | if (copy_from_user(cmd, buf, len)) | ||
455 | return -EFAULT; | ||
456 | cmd[len] = '\0'; | ||
457 | |||
458 | if (sscanf(cmd, " brightness : %i", &value) == 1 && | ||
467 | value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) { | 459 | value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) { |
468 | ret = set_lcd(value); | 460 | ret = set_lcd(value); |
469 | if (ret == 0) | 461 | if (ret == 0) |
@@ -474,7 +466,16 @@ static unsigned long write_lcd(const char *buffer, unsigned long count) | |||
474 | return ret; | 466 | return ret; |
475 | } | 467 | } |
476 | 468 | ||
477 | static char *read_video(char *p) | 469 | static const struct file_operations lcd_proc_fops = { |
470 | .owner = THIS_MODULE, | ||
471 | .open = lcd_proc_open, | ||
472 | .read = seq_read, | ||
473 | .llseek = seq_lseek, | ||
474 | .release = single_release, | ||
475 | .write = lcd_proc_write, | ||
476 | }; | ||
477 | |||
478 | static int video_proc_show(struct seq_file *m, void *v) | ||
478 | { | 479 | { |
479 | u32 hci_result; | 480 | u32 hci_result; |
480 | u32 value; | 481 | u32 value; |
@@ -484,18 +485,25 @@ static char *read_video(char *p) | |||
484 | int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0; | 485 | int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0; |
485 | int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0; | 486 | int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0; |
486 | int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0; | 487 | int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0; |
487 | p += sprintf(p, "lcd_out: %d\n", is_lcd); | 488 | seq_printf(m, "lcd_out: %d\n", is_lcd); |
488 | p += sprintf(p, "crt_out: %d\n", is_crt); | 489 | seq_printf(m, "crt_out: %d\n", is_crt); |
489 | p += sprintf(p, "tv_out: %d\n", is_tv); | 490 | seq_printf(m, "tv_out: %d\n", is_tv); |
490 | } else { | 491 | } else { |
491 | printk(MY_ERR "Error reading video out status\n"); | 492 | printk(MY_ERR "Error reading video out status\n"); |
492 | } | 493 | } |
493 | 494 | ||
494 | return p; | 495 | return 0; |
495 | } | 496 | } |
496 | 497 | ||
497 | static unsigned long write_video(const char *buffer, unsigned long count) | 498 | static int video_proc_open(struct inode *inode, struct file *file) |
498 | { | 499 | { |
500 | return single_open(file, video_proc_show, NULL); | ||
501 | } | ||
502 | |||
503 | static ssize_t video_proc_write(struct file *file, const char __user *buf, | ||
504 | size_t count, loff_t *pos) | ||
505 | { | ||
506 | char *cmd, *buffer; | ||
499 | int value; | 507 | int value; |
500 | int remain = count; | 508 | int remain = count; |
501 | int lcd_out = -1; | 509 | int lcd_out = -1; |
@@ -504,6 +512,17 @@ static unsigned long write_video(const char *buffer, unsigned long count) | |||
504 | u32 hci_result; | 512 | u32 hci_result; |
505 | u32 video_out; | 513 | u32 video_out; |
506 | 514 | ||
515 | cmd = kmalloc(count + 1, GFP_KERNEL); | ||
516 | if (!cmd) | ||
517 | return -ENOMEM; | ||
518 | if (copy_from_user(cmd, buf, count)) { | ||
519 | kfree(cmd); | ||
520 | return -EFAULT; | ||
521 | } | ||
522 | cmd[count] = '\0'; | ||
523 | |||
524 | buffer = cmd; | ||
525 | |||
507 | /* scan expression. Multiple expressions may be delimited with ; | 526 | /* scan expression. Multiple expressions may be delimited with ; |
508 | * | 527 | * |
509 | * NOTE: to keep scanning simple, invalid fields are ignored | 528 | * NOTE: to keep scanning simple, invalid fields are ignored |
@@ -523,6 +542,8 @@ static unsigned long write_video(const char *buffer, unsigned long count) | |||
523 | while (remain && *(buffer - 1) != ';'); | 542 | while (remain && *(buffer - 1) != ';'); |
524 | } | 543 | } |
525 | 544 | ||
545 | kfree(cmd); | ||
546 | |||
526 | hci_read1(HCI_VIDEO_OUT, &video_out, &hci_result); | 547 | hci_read1(HCI_VIDEO_OUT, &video_out, &hci_result); |
527 | if (hci_result == HCI_SUCCESS) { | 548 | if (hci_result == HCI_SUCCESS) { |
528 | unsigned int new_video_out = video_out; | 549 | unsigned int new_video_out = video_out; |
@@ -543,28 +564,50 @@ static unsigned long write_video(const char *buffer, unsigned long count) | |||
543 | return count; | 564 | return count; |
544 | } | 565 | } |
545 | 566 | ||
546 | static char *read_fan(char *p) | 567 | static const struct file_operations video_proc_fops = { |
568 | .owner = THIS_MODULE, | ||
569 | .open = video_proc_open, | ||
570 | .read = seq_read, | ||
571 | .llseek = seq_lseek, | ||
572 | .release = single_release, | ||
573 | .write = video_proc_write, | ||
574 | }; | ||
575 | |||
576 | static int fan_proc_show(struct seq_file *m, void *v) | ||
547 | { | 577 | { |
548 | u32 hci_result; | 578 | u32 hci_result; |
549 | u32 value; | 579 | u32 value; |
550 | 580 | ||
551 | hci_read1(HCI_FAN, &value, &hci_result); | 581 | hci_read1(HCI_FAN, &value, &hci_result); |
552 | if (hci_result == HCI_SUCCESS) { | 582 | if (hci_result == HCI_SUCCESS) { |
553 | p += sprintf(p, "running: %d\n", (value > 0)); | 583 | seq_printf(m, "running: %d\n", (value > 0)); |
554 | p += sprintf(p, "force_on: %d\n", force_fan); | 584 | seq_printf(m, "force_on: %d\n", force_fan); |
555 | } else { | 585 | } else { |
556 | printk(MY_ERR "Error reading fan status\n"); | 586 | printk(MY_ERR "Error reading fan status\n"); |
557 | } | 587 | } |
558 | 588 | ||
559 | return p; | 589 | return 0; |
590 | } | ||
591 | |||
592 | static int fan_proc_open(struct inode *inode, struct file *file) | ||
593 | { | ||
594 | return single_open(file, fan_proc_show, NULL); | ||
560 | } | 595 | } |
561 | 596 | ||
562 | static unsigned long write_fan(const char *buffer, unsigned long count) | 597 | static ssize_t fan_proc_write(struct file *file, const char __user *buf, |
598 | size_t count, loff_t *pos) | ||
563 | { | 599 | { |
600 | char cmd[42]; | ||
601 | size_t len; | ||
564 | int value; | 602 | int value; |
565 | u32 hci_result; | 603 | u32 hci_result; |
566 | 604 | ||
567 | if (sscanf(buffer, " force_on : %i", &value) == 1 && | 605 | len = min(count, sizeof(cmd) - 1); |
606 | if (copy_from_user(cmd, buf, len)) | ||
607 | return -EFAULT; | ||
608 | cmd[len] = '\0'; | ||
609 | |||
610 | if (sscanf(cmd, " force_on : %i", &value) == 1 && | ||
568 | value >= 0 && value <= 1) { | 611 | value >= 0 && value <= 1) { |
569 | hci_write1(HCI_FAN, value, &hci_result); | 612 | hci_write1(HCI_FAN, value, &hci_result); |
570 | if (hci_result != HCI_SUCCESS) | 613 | if (hci_result != HCI_SUCCESS) |
@@ -578,7 +621,16 @@ static unsigned long write_fan(const char *buffer, unsigned long count) | |||
578 | return count; | 621 | return count; |
579 | } | 622 | } |
580 | 623 | ||
581 | static char *read_keys(char *p) | 624 | static const struct file_operations fan_proc_fops = { |
625 | .owner = THIS_MODULE, | ||
626 | .open = fan_proc_open, | ||
627 | .read = seq_read, | ||
628 | .llseek = seq_lseek, | ||
629 | .release = single_release, | ||
630 | .write = fan_proc_write, | ||
631 | }; | ||
632 | |||
633 | static int keys_proc_show(struct seq_file *m, void *v) | ||
582 | { | 634 | { |
583 | u32 hci_result; | 635 | u32 hci_result; |
584 | u32 value; | 636 | u32 value; |
@@ -602,18 +654,30 @@ static char *read_keys(char *p) | |||
602 | } | 654 | } |
603 | } | 655 | } |
604 | 656 | ||
605 | p += sprintf(p, "hotkey_ready: %d\n", key_event_valid); | 657 | seq_printf(m, "hotkey_ready: %d\n", key_event_valid); |
606 | p += sprintf(p, "hotkey: 0x%04x\n", last_key_event); | 658 | seq_printf(m, "hotkey: 0x%04x\n", last_key_event); |
659 | end: | ||
660 | return 0; | ||
661 | } | ||
607 | 662 | ||
608 | end: | 663 | static int keys_proc_open(struct inode *inode, struct file *file) |
609 | return p; | 664 | { |
665 | return single_open(file, keys_proc_show, NULL); | ||
610 | } | 666 | } |
611 | 667 | ||
612 | static unsigned long write_keys(const char *buffer, unsigned long count) | 668 | static ssize_t keys_proc_write(struct file *file, const char __user *buf, |
669 | size_t count, loff_t *pos) | ||
613 | { | 670 | { |
671 | char cmd[42]; | ||
672 | size_t len; | ||
614 | int value; | 673 | int value; |
615 | 674 | ||
616 | if (sscanf(buffer, " hotkey_ready : %i", &value) == 1 && value == 0) { | 675 | len = min(count, sizeof(cmd) - 1); |
676 | if (copy_from_user(cmd, buf, len)) | ||
677 | return -EFAULT; | ||
678 | cmd[len] = '\0'; | ||
679 | |||
680 | if (sscanf(cmd, " hotkey_ready : %i", &value) == 1 && value == 0) { | ||
617 | key_event_valid = 0; | 681 | key_event_valid = 0; |
618 | } else { | 682 | } else { |
619 | return -EINVAL; | 683 | return -EINVAL; |
@@ -622,52 +686,58 @@ static unsigned long write_keys(const char *buffer, unsigned long count) | |||
622 | return count; | 686 | return count; |
623 | } | 687 | } |
624 | 688 | ||
625 | static char *read_version(char *p) | 689 | static const struct file_operations keys_proc_fops = { |
690 | .owner = THIS_MODULE, | ||
691 | .open = keys_proc_open, | ||
692 | .read = seq_read, | ||
693 | .llseek = seq_lseek, | ||
694 | .release = single_release, | ||
695 | .write = keys_proc_write, | ||
696 | }; | ||
697 | |||
698 | static int version_proc_show(struct seq_file *m, void *v) | ||
699 | { | ||
700 | seq_printf(m, "driver: %s\n", TOSHIBA_ACPI_VERSION); | ||
701 | seq_printf(m, "proc_interface: %d\n", PROC_INTERFACE_VERSION); | ||
702 | return 0; | ||
703 | } | ||
704 | |||
705 | static int version_proc_open(struct inode *inode, struct file *file) | ||
626 | { | 706 | { |
627 | p += sprintf(p, "driver: %s\n", TOSHIBA_ACPI_VERSION); | 707 | return single_open(file, version_proc_show, PDE(inode)->data); |
628 | p += sprintf(p, "proc_interface: %d\n", | ||
629 | PROC_INTERFACE_VERSION); | ||
630 | return p; | ||
631 | } | 708 | } |
632 | 709 | ||
710 | static const struct file_operations version_proc_fops = { | ||
711 | .owner = THIS_MODULE, | ||
712 | .open = version_proc_open, | ||
713 | .read = seq_read, | ||
714 | .llseek = seq_lseek, | ||
715 | .release = single_release, | ||
716 | }; | ||
717 | |||
633 | /* proc and module init | 718 | /* proc and module init |
634 | */ | 719 | */ |
635 | 720 | ||
636 | #define PROC_TOSHIBA "toshiba" | 721 | #define PROC_TOSHIBA "toshiba" |
637 | 722 | ||
638 | static ProcItem proc_items[] = { | ||
639 | {"lcd", read_lcd, write_lcd}, | ||
640 | {"video", read_video, write_video}, | ||
641 | {"fan", read_fan, write_fan}, | ||
642 | {"keys", read_keys, write_keys}, | ||
643 | {"version", read_version, NULL}, | ||
644 | {NULL} | ||
645 | }; | ||
646 | |||
647 | static acpi_status __init add_device(void) | 723 | static acpi_status __init add_device(void) |
648 | { | 724 | { |
649 | struct proc_dir_entry *proc; | 725 | proc_create("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, &lcd_proc_fops); |
650 | ProcItem *item; | 726 | proc_create("video", S_IRUGO | S_IWUSR, toshiba_proc_dir, &video_proc_fops); |
651 | 727 | proc_create("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir, &fan_proc_fops); | |
652 | for (item = proc_items; item->name; ++item) { | 728 | proc_create("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir, &keys_proc_fops); |
653 | proc = create_proc_read_entry(item->name, | 729 | proc_create("version", S_IRUGO, toshiba_proc_dir, &version_proc_fops); |
654 | S_IFREG | S_IRUGO | S_IWUSR, | ||
655 | toshiba_proc_dir, | ||
656 | (read_proc_t *) dispatch_read, | ||
657 | item); | ||
658 | if (proc && item->write_func) | ||
659 | proc->write_proc = (write_proc_t *) dispatch_write; | ||
660 | } | ||
661 | 730 | ||
662 | return AE_OK; | 731 | return AE_OK; |
663 | } | 732 | } |
664 | 733 | ||
665 | static acpi_status remove_device(void) | 734 | static acpi_status remove_device(void) |
666 | { | 735 | { |
667 | ProcItem *item; | 736 | remove_proc_entry("lcd", toshiba_proc_dir); |
668 | 737 | remove_proc_entry("video", toshiba_proc_dir); | |
669 | for (item = proc_items; item->name; ++item) | 738 | remove_proc_entry("fan", toshiba_proc_dir); |
670 | remove_proc_entry(item->name, toshiba_proc_dir); | 739 | remove_proc_entry("keys", toshiba_proc_dir); |
740 | remove_proc_entry("version", toshiba_proc_dir); | ||
671 | return AE_OK; | 741 | return AE_OK; |
672 | } | 742 | } |
673 | 743 | ||
@@ -676,8 +746,158 @@ static struct backlight_ops toshiba_backlight_data = { | |||
676 | .update_status = set_lcd_status, | 746 | .update_status = set_lcd_status, |
677 | }; | 747 | }; |
678 | 748 | ||
749 | static struct key_entry *toshiba_acpi_get_entry_by_scancode(unsigned int code) | ||
750 | { | ||
751 | struct key_entry *key; | ||
752 | |||
753 | for (key = toshiba_acpi_keymap; key->type != KE_END; key++) | ||
754 | if (code == key->code) | ||
755 | return key; | ||
756 | |||
757 | return NULL; | ||
758 | } | ||
759 | |||
760 | static struct key_entry *toshiba_acpi_get_entry_by_keycode(unsigned int code) | ||
761 | { | ||
762 | struct key_entry *key; | ||
763 | |||
764 | for (key = toshiba_acpi_keymap; key->type != KE_END; key++) | ||
765 | if (code == key->keycode && key->type == KE_KEY) | ||
766 | return key; | ||
767 | |||
768 | return NULL; | ||
769 | } | ||
770 | |||
771 | static int toshiba_acpi_getkeycode(struct input_dev *dev, | ||
772 | unsigned int scancode, unsigned int *keycode) | ||
773 | { | ||
774 | struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode); | ||
775 | |||
776 | if (key && key->type == KE_KEY) { | ||
777 | *keycode = key->keycode; | ||
778 | return 0; | ||
779 | } | ||
780 | |||
781 | return -EINVAL; | ||
782 | } | ||
783 | |||
784 | static int toshiba_acpi_setkeycode(struct input_dev *dev, | ||
785 | unsigned int scancode, unsigned int keycode) | ||
786 | { | ||
787 | struct key_entry *key; | ||
788 | unsigned int old_keycode; | ||
789 | |||
790 | key = toshiba_acpi_get_entry_by_scancode(scancode); | ||
791 | if (key && key->type == KE_KEY) { | ||
792 | old_keycode = key->keycode; | ||
793 | key->keycode = keycode; | ||
794 | set_bit(keycode, dev->keybit); | ||
795 | if (!toshiba_acpi_get_entry_by_keycode(old_keycode)) | ||
796 | clear_bit(old_keycode, dev->keybit); | ||
797 | return 0; | ||
798 | } | ||
799 | |||
800 | return -EINVAL; | ||
801 | } | ||
802 | |||
803 | static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) | ||
804 | { | ||
805 | u32 hci_result, value; | ||
806 | struct key_entry *key; | ||
807 | |||
808 | if (event != 0x80) | ||
809 | return; | ||
810 | do { | ||
811 | hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result); | ||
812 | if (hci_result == HCI_SUCCESS) { | ||
813 | if (value == 0x100) | ||
814 | continue; | ||
815 | /* act on key press; ignore key release */ | ||
816 | if (value & 0x80) | ||
817 | continue; | ||
818 | |||
819 | key = toshiba_acpi_get_entry_by_scancode | ||
820 | (value); | ||
821 | if (!key) { | ||
822 | printk(MY_INFO "Unknown key %x\n", | ||
823 | value); | ||
824 | continue; | ||
825 | } | ||
826 | input_report_key(toshiba_acpi.hotkey_dev, | ||
827 | key->keycode, 1); | ||
828 | input_sync(toshiba_acpi.hotkey_dev); | ||
829 | input_report_key(toshiba_acpi.hotkey_dev, | ||
830 | key->keycode, 0); | ||
831 | input_sync(toshiba_acpi.hotkey_dev); | ||
832 | } else if (hci_result == HCI_NOT_SUPPORTED) { | ||
833 | /* This is a workaround for an unresolved issue on | ||
834 | * some machines where system events sporadically | ||
835 | * become disabled. */ | ||
836 | hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); | ||
837 | printk(MY_NOTICE "Re-enabled hotkeys\n"); | ||
838 | } | ||
839 | } while (hci_result != HCI_EMPTY); | ||
840 | } | ||
841 | |||
842 | static int toshiba_acpi_setup_keyboard(char *device) | ||
843 | { | ||
844 | acpi_status status; | ||
845 | acpi_handle handle; | ||
846 | int result; | ||
847 | const struct key_entry *key; | ||
848 | |||
849 | status = acpi_get_handle(NULL, device, &handle); | ||
850 | if (ACPI_FAILURE(status)) { | ||
851 | printk(MY_INFO "Unable to get notification device\n"); | ||
852 | return -ENODEV; | ||
853 | } | ||
854 | |||
855 | toshiba_acpi.handle = handle; | ||
856 | |||
857 | status = acpi_evaluate_object(handle, "ENAB", NULL, NULL); | ||
858 | if (ACPI_FAILURE(status)) { | ||
859 | printk(MY_INFO "Unable to enable hotkeys\n"); | ||
860 | return -ENODEV; | ||
861 | } | ||
862 | |||
863 | status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY, | ||
864 | toshiba_acpi_notify, NULL); | ||
865 | if (ACPI_FAILURE(status)) { | ||
866 | printk(MY_INFO "Unable to install hotkey notification\n"); | ||
867 | return -ENODEV; | ||
868 | } | ||
869 | |||
870 | toshiba_acpi.hotkey_dev = input_allocate_device(); | ||
871 | if (!toshiba_acpi.hotkey_dev) { | ||
872 | printk(MY_INFO "Unable to register input device\n"); | ||
873 | return -ENOMEM; | ||
874 | } | ||
875 | |||
876 | toshiba_acpi.hotkey_dev->name = "Toshiba input device"; | ||
877 | toshiba_acpi.hotkey_dev->phys = device; | ||
878 | toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST; | ||
879 | toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode; | ||
880 | toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode; | ||
881 | |||
882 | for (key = toshiba_acpi_keymap; key->type != KE_END; key++) { | ||
883 | set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit); | ||
884 | set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit); | ||
885 | } | ||
886 | |||
887 | result = input_register_device(toshiba_acpi.hotkey_dev); | ||
888 | if (result) { | ||
889 | printk(MY_INFO "Unable to register input device\n"); | ||
890 | return result; | ||
891 | } | ||
892 | |||
893 | return 0; | ||
894 | } | ||
895 | |||
679 | static void toshiba_acpi_exit(void) | 896 | static void toshiba_acpi_exit(void) |
680 | { | 897 | { |
898 | if (toshiba_acpi.hotkey_dev) | ||
899 | input_unregister_device(toshiba_acpi.hotkey_dev); | ||
900 | |||
681 | if (toshiba_acpi.bt_rfk) { | 901 | if (toshiba_acpi.bt_rfk) { |
682 | rfkill_unregister(toshiba_acpi.bt_rfk); | 902 | rfkill_unregister(toshiba_acpi.bt_rfk); |
683 | rfkill_destroy(toshiba_acpi.bt_rfk); | 903 | rfkill_destroy(toshiba_acpi.bt_rfk); |
@@ -691,6 +911,9 @@ static void toshiba_acpi_exit(void) | |||
691 | if (toshiba_proc_dir) | 911 | if (toshiba_proc_dir) |
692 | remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); | 912 | remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); |
693 | 913 | ||
914 | acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY, | ||
915 | toshiba_acpi_notify); | ||
916 | |||
694 | platform_device_unregister(toshiba_acpi.p_dev); | 917 | platform_device_unregister(toshiba_acpi.p_dev); |
695 | 918 | ||
696 | return; | 919 | return; |
@@ -702,16 +925,21 @@ static int __init toshiba_acpi_init(void) | |||
702 | u32 hci_result; | 925 | u32 hci_result; |
703 | bool bt_present; | 926 | bool bt_present; |
704 | int ret = 0; | 927 | int ret = 0; |
928 | struct backlight_properties props; | ||
705 | 929 | ||
706 | if (acpi_disabled) | 930 | if (acpi_disabled) |
707 | return -ENODEV; | 931 | return -ENODEV; |
708 | 932 | ||
709 | /* simple device detection: look for HCI method */ | 933 | /* simple device detection: look for HCI method */ |
710 | if (is_valid_acpi_path(METHOD_HCI_1)) | 934 | if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) { |
711 | method_hci = METHOD_HCI_1; | 935 | method_hci = TOSH_INTERFACE_1 GHCI_METHOD; |
712 | else if (is_valid_acpi_path(METHOD_HCI_2)) | 936 | if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1)) |
713 | method_hci = METHOD_HCI_2; | 937 | printk(MY_INFO "Unable to activate hotkeys\n"); |
714 | else | 938 | } else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) { |
939 | method_hci = TOSH_INTERFACE_2 GHCI_METHOD; | ||
940 | if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2)) | ||
941 | printk(MY_INFO "Unable to activate hotkeys\n"); | ||
942 | } else | ||
715 | return -ENODEV; | 943 | return -ENODEV; |
716 | 944 | ||
717 | printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n", | 945 | printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n", |
@@ -748,10 +976,12 @@ static int __init toshiba_acpi_init(void) | |||
748 | } | 976 | } |
749 | } | 977 | } |
750 | 978 | ||
979 | props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; | ||
751 | toshiba_backlight_device = backlight_device_register("toshiba", | 980 | toshiba_backlight_device = backlight_device_register("toshiba", |
752 | &toshiba_acpi.p_dev->dev, | 981 | &toshiba_acpi.p_dev->dev, |
753 | NULL, | 982 | NULL, |
754 | &toshiba_backlight_data); | 983 | &toshiba_backlight_data, |
984 | &props); | ||
755 | if (IS_ERR(toshiba_backlight_device)) { | 985 | if (IS_ERR(toshiba_backlight_device)) { |
756 | ret = PTR_ERR(toshiba_backlight_device); | 986 | ret = PTR_ERR(toshiba_backlight_device); |
757 | 987 | ||
@@ -760,7 +990,6 @@ static int __init toshiba_acpi_init(void) | |||
760 | toshiba_acpi_exit(); | 990 | toshiba_acpi_exit(); |
761 | return ret; | 991 | return ret; |
762 | } | 992 | } |
763 | toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; | ||
764 | 993 | ||
765 | /* Register rfkill switch for Bluetooth */ | 994 | /* Register rfkill switch for Bluetooth */ |
766 | if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) { | 995 | if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) { |