diff options
Diffstat (limited to 'drivers/input/mouse/synaptics.c')
-rw-r--r-- | drivers/input/mouse/synaptics.c | 77 |
1 files changed, 61 insertions, 16 deletions
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index f4a61252bcc9..ebd7a99efeae 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c | |||
@@ -24,9 +24,11 @@ | |||
24 | */ | 24 | */ |
25 | 25 | ||
26 | #include <linux/module.h> | 26 | #include <linux/module.h> |
27 | #include <linux/dmi.h> | ||
27 | #include <linux/input.h> | 28 | #include <linux/input.h> |
28 | #include <linux/serio.h> | 29 | #include <linux/serio.h> |
29 | #include <linux/libps2.h> | 30 | #include <linux/libps2.h> |
31 | #include <linux/slab.h> | ||
30 | #include "psmouse.h" | 32 | #include "psmouse.h" |
31 | #include "synaptics.h" | 33 | #include "synaptics.h" |
32 | 34 | ||
@@ -135,7 +137,8 @@ static int synaptics_capability(struct psmouse *psmouse) | |||
135 | if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap)) | 137 | if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap)) |
136 | return -1; | 138 | return -1; |
137 | priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2]; | 139 | priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2]; |
138 | priv->ext_cap = 0; | 140 | priv->ext_cap = priv->ext_cap_0c = 0; |
141 | |||
139 | if (!SYN_CAP_VALID(priv->capabilities)) | 142 | if (!SYN_CAP_VALID(priv->capabilities)) |
140 | return -1; | 143 | return -1; |
141 | 144 | ||
@@ -148,7 +151,7 @@ static int synaptics_capability(struct psmouse *psmouse) | |||
148 | if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) { | 151 | if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) { |
149 | if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) { | 152 | if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) { |
150 | printk(KERN_ERR "Synaptics claims to have extended capabilities," | 153 | printk(KERN_ERR "Synaptics claims to have extended capabilities," |
151 | " but I'm not able to read them."); | 154 | " but I'm not able to read them.\n"); |
152 | } else { | 155 | } else { |
153 | priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2]; | 156 | priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2]; |
154 | 157 | ||
@@ -160,6 +163,16 @@ static int synaptics_capability(struct psmouse *psmouse) | |||
160 | priv->ext_cap &= 0xff0fff; | 163 | priv->ext_cap &= 0xff0fff; |
161 | } | 164 | } |
162 | } | 165 | } |
166 | |||
167 | if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 4) { | ||
168 | if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB_0C, cap)) { | ||
169 | printk(KERN_ERR "Synaptics claims to have extended capability 0x0c," | ||
170 | " but I'm not able to read it.\n"); | ||
171 | } else { | ||
172 | priv->ext_cap_0c = (cap[0] << 16) | (cap[1] << 8) | cap[2]; | ||
173 | } | ||
174 | } | ||
175 | |||
163 | return 0; | 176 | return 0; |
164 | } | 177 | } |
165 | 178 | ||
@@ -346,7 +359,15 @@ static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data | |||
346 | hw->left = (buf[0] & 0x01) ? 1 : 0; | 359 | hw->left = (buf[0] & 0x01) ? 1 : 0; |
347 | hw->right = (buf[0] & 0x02) ? 1 : 0; | 360 | hw->right = (buf[0] & 0x02) ? 1 : 0; |
348 | 361 | ||
349 | if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) { | 362 | if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { |
363 | /* | ||
364 | * Clickpad's button is transmitted as middle button, | ||
365 | * however, since it is primary button, we will report | ||
366 | * it as BTN_LEFT. | ||
367 | */ | ||
368 | hw->left = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; | ||
369 | |||
370 | } else if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) { | ||
350 | hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; | 371 | hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; |
351 | if (hw->w == 2) | 372 | if (hw->w == 2) |
352 | hw->scroll = (signed char)(buf[1]); | 373 | hw->scroll = (signed char)(buf[1]); |
@@ -591,6 +612,12 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) | |||
591 | 612 | ||
592 | dev->absres[ABS_X] = priv->x_res; | 613 | dev->absres[ABS_X] = priv->x_res; |
593 | dev->absres[ABS_Y] = priv->y_res; | 614 | dev->absres[ABS_Y] = priv->y_res; |
615 | |||
616 | if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { | ||
617 | /* Clickpads report only left button */ | ||
618 | __clear_bit(BTN_RIGHT, dev->keybit); | ||
619 | __clear_bit(BTN_MIDDLE, dev->keybit); | ||
620 | } | ||
594 | } | 621 | } |
595 | 622 | ||
596 | static void synaptics_disconnect(struct psmouse *psmouse) | 623 | static void synaptics_disconnect(struct psmouse *psmouse) |
@@ -629,25 +656,26 @@ static int synaptics_reconnect(struct psmouse *psmouse) | |||
629 | return 0; | 656 | return 0; |
630 | } | 657 | } |
631 | 658 | ||
632 | #if defined(__i386__) | 659 | static bool impaired_toshiba_kbc; |
633 | #include <linux/dmi.h> | 660 | |
634 | static const struct dmi_system_id toshiba_dmi_table[] = { | 661 | static const struct dmi_system_id __initconst toshiba_dmi_table[] = { |
662 | #if defined(CONFIG_DMI) && defined(CONFIG_X86) | ||
635 | { | 663 | { |
636 | .ident = "Toshiba Satellite", | 664 | /* Toshiba Satellite */ |
637 | .matches = { | 665 | .matches = { |
638 | DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), | 666 | DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), |
639 | DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"), | 667 | DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"), |
640 | }, | 668 | }, |
641 | }, | 669 | }, |
642 | { | 670 | { |
643 | .ident = "Toshiba Dynabook", | 671 | /* Toshiba Dynabook */ |
644 | .matches = { | 672 | .matches = { |
645 | DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), | 673 | DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), |
646 | DMI_MATCH(DMI_PRODUCT_NAME, "dynabook"), | 674 | DMI_MATCH(DMI_PRODUCT_NAME, "dynabook"), |
647 | }, | 675 | }, |
648 | }, | 676 | }, |
649 | { | 677 | { |
650 | .ident = "Toshiba Portege M300", | 678 | /* Toshiba Portege M300 */ |
651 | .matches = { | 679 | .matches = { |
652 | DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), | 680 | DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), |
653 | DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M300"), | 681 | DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M300"), |
@@ -655,7 +683,7 @@ static const struct dmi_system_id toshiba_dmi_table[] = { | |||
655 | 683 | ||
656 | }, | 684 | }, |
657 | { | 685 | { |
658 | .ident = "Toshiba Portege M300", | 686 | /* Toshiba Portege M300 */ |
659 | .matches = { | 687 | .matches = { |
660 | DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), | 688 | DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), |
661 | DMI_MATCH(DMI_PRODUCT_NAME, "Portable PC"), | 689 | DMI_MATCH(DMI_PRODUCT_NAME, "Portable PC"), |
@@ -664,8 +692,13 @@ static const struct dmi_system_id toshiba_dmi_table[] = { | |||
664 | 692 | ||
665 | }, | 693 | }, |
666 | { } | 694 | { } |
667 | }; | ||
668 | #endif | 695 | #endif |
696 | }; | ||
697 | |||
698 | void __init synaptics_module_init(void) | ||
699 | { | ||
700 | impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table); | ||
701 | } | ||
669 | 702 | ||
670 | int synaptics_init(struct psmouse *psmouse) | 703 | int synaptics_init(struct psmouse *psmouse) |
671 | { | 704 | { |
@@ -689,10 +722,10 @@ int synaptics_init(struct psmouse *psmouse) | |||
689 | 722 | ||
690 | priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS; | 723 | priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS; |
691 | 724 | ||
692 | printk(KERN_INFO "Synaptics Touchpad, model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx\n", | 725 | printk(KERN_INFO "Synaptics Touchpad, model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx\n", |
693 | SYN_ID_MODEL(priv->identity), | 726 | SYN_ID_MODEL(priv->identity), |
694 | SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity), | 727 | SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity), |
695 | priv->model_id, priv->capabilities, priv->ext_cap); | 728 | priv->model_id, priv->capabilities, priv->ext_cap, priv->ext_cap_0c); |
696 | 729 | ||
697 | set_input_params(psmouse->dev, priv); | 730 | set_input_params(psmouse->dev, priv); |
698 | 731 | ||
@@ -718,18 +751,16 @@ int synaptics_init(struct psmouse *psmouse) | |||
718 | if (SYN_CAP_PASS_THROUGH(priv->capabilities)) | 751 | if (SYN_CAP_PASS_THROUGH(priv->capabilities)) |
719 | synaptics_pt_create(psmouse); | 752 | synaptics_pt_create(psmouse); |
720 | 753 | ||
721 | #if defined(__i386__) | ||
722 | /* | 754 | /* |
723 | * Toshiba's KBC seems to have trouble handling data from | 755 | * Toshiba's KBC seems to have trouble handling data from |
724 | * Synaptics as full rate, switch to lower rate which is roughly | 756 | * Synaptics as full rate, switch to lower rate which is roughly |
725 | * thye same as rate of standard PS/2 mouse. | 757 | * thye same as rate of standard PS/2 mouse. |
726 | */ | 758 | */ |
727 | if (psmouse->rate >= 80 && dmi_check_system(toshiba_dmi_table)) { | 759 | if (psmouse->rate >= 80 && impaired_toshiba_kbc) { |
728 | printk(KERN_INFO "synaptics: Toshiba %s detected, limiting rate to 40pps.\n", | 760 | printk(KERN_INFO "synaptics: Toshiba %s detected, limiting rate to 40pps.\n", |
729 | dmi_get_system_info(DMI_PRODUCT_NAME)); | 761 | dmi_get_system_info(DMI_PRODUCT_NAME)); |
730 | psmouse->rate = 40; | 762 | psmouse->rate = 40; |
731 | } | 763 | } |
732 | #endif | ||
733 | 764 | ||
734 | return 0; | 765 | return 0; |
735 | 766 | ||
@@ -738,12 +769,26 @@ int synaptics_init(struct psmouse *psmouse) | |||
738 | return -1; | 769 | return -1; |
739 | } | 770 | } |
740 | 771 | ||
772 | bool synaptics_supported(void) | ||
773 | { | ||
774 | return true; | ||
775 | } | ||
776 | |||
741 | #else /* CONFIG_MOUSE_PS2_SYNAPTICS */ | 777 | #else /* CONFIG_MOUSE_PS2_SYNAPTICS */ |
742 | 778 | ||
779 | void __init synaptics_module_init(void) | ||
780 | { | ||
781 | } | ||
782 | |||
743 | int synaptics_init(struct psmouse *psmouse) | 783 | int synaptics_init(struct psmouse *psmouse) |
744 | { | 784 | { |
745 | return -ENOSYS; | 785 | return -ENOSYS; |
746 | } | 786 | } |
747 | 787 | ||
788 | bool synaptics_supported(void) | ||
789 | { | ||
790 | return false; | ||
791 | } | ||
792 | |||
748 | #endif /* CONFIG_MOUSE_PS2_SYNAPTICS */ | 793 | #endif /* CONFIG_MOUSE_PS2_SYNAPTICS */ |
749 | 794 | ||