diff options
Diffstat (limited to 'drivers/input/mouse/psmouse-base.c')
-rw-r--r-- | drivers/input/mouse/psmouse-base.c | 54 |
1 files changed, 44 insertions, 10 deletions
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 0ecf1297b6a8..259e6b70544b 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c | |||
@@ -68,6 +68,15 @@ __obsolete_setup("psmouse_smartscroll="); | |||
68 | __obsolete_setup("psmouse_resetafter="); | 68 | __obsolete_setup("psmouse_resetafter="); |
69 | __obsolete_setup("psmouse_rate="); | 69 | __obsolete_setup("psmouse_rate="); |
70 | 70 | ||
71 | /* | ||
72 | * psmouse_sem protects all operations changing state of mouse | ||
73 | * (connecting, disconnecting, changing rate or resolution via | ||
74 | * sysfs). We could use a per-device semaphore but since there | ||
75 | * rarely more than one PS/2 mouse connected and since semaphore | ||
76 | * is taken in "slow" paths it is not worth it. | ||
77 | */ | ||
78 | static DECLARE_MUTEX(psmouse_sem); | ||
79 | |||
71 | static char *psmouse_protocols[] = { "None", "PS/2", "PS2++", "ThinkPS/2", "GenPS/2", "ImPS/2", "ImExPS/2", "SynPS/2", "AlpsPS/2", "LBPS/2" }; | 80 | static char *psmouse_protocols[] = { "None", "PS/2", "PS2++", "ThinkPS/2", "GenPS/2", "ImPS/2", "ImExPS/2", "SynPS/2", "AlpsPS/2", "LBPS/2" }; |
72 | 81 | ||
73 | /* | 82 | /* |
@@ -667,30 +676,40 @@ static void psmouse_cleanup(struct serio *serio) | |||
667 | 676 | ||
668 | static void psmouse_disconnect(struct serio *serio) | 677 | static void psmouse_disconnect(struct serio *serio) |
669 | { | 678 | { |
670 | struct psmouse *psmouse, *parent; | 679 | struct psmouse *psmouse, *parent = NULL; |
680 | |||
681 | psmouse = serio_get_drvdata(serio); | ||
671 | 682 | ||
672 | device_remove_file(&serio->dev, &psmouse_attr_rate); | 683 | device_remove_file(&serio->dev, &psmouse_attr_rate); |
673 | device_remove_file(&serio->dev, &psmouse_attr_resolution); | 684 | device_remove_file(&serio->dev, &psmouse_attr_resolution); |
674 | device_remove_file(&serio->dev, &psmouse_attr_resetafter); | 685 | device_remove_file(&serio->dev, &psmouse_attr_resetafter); |
675 | 686 | ||
676 | psmouse = serio_get_drvdata(serio); | 687 | down(&psmouse_sem); |
688 | |||
677 | psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); | 689 | psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); |
678 | 690 | ||
679 | if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { | 691 | if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { |
680 | parent = serio_get_drvdata(serio->parent); | 692 | parent = serio_get_drvdata(serio->parent); |
681 | if (parent->pt_deactivate) | 693 | psmouse_deactivate(parent); |
682 | parent->pt_deactivate(parent); | ||
683 | } | 694 | } |
684 | 695 | ||
685 | if (psmouse->disconnect) | 696 | if (psmouse->disconnect) |
686 | psmouse->disconnect(psmouse); | 697 | psmouse->disconnect(psmouse); |
687 | 698 | ||
699 | if (parent && parent->pt_deactivate) | ||
700 | parent->pt_deactivate(parent); | ||
701 | |||
688 | psmouse_set_state(psmouse, PSMOUSE_IGNORE); | 702 | psmouse_set_state(psmouse, PSMOUSE_IGNORE); |
689 | 703 | ||
690 | input_unregister_device(&psmouse->dev); | 704 | input_unregister_device(&psmouse->dev); |
691 | serio_close(serio); | 705 | serio_close(serio); |
692 | serio_set_drvdata(serio, NULL); | 706 | serio_set_drvdata(serio, NULL); |
693 | kfree(psmouse); | 707 | kfree(psmouse); |
708 | |||
709 | if (parent) | ||
710 | psmouse_activate(parent); | ||
711 | |||
712 | up(&psmouse_sem); | ||
694 | } | 713 | } |
695 | 714 | ||
696 | /* | 715 | /* |
@@ -702,6 +721,8 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) | |||
702 | struct psmouse *psmouse, *parent = NULL; | 721 | struct psmouse *psmouse, *parent = NULL; |
703 | int retval; | 722 | int retval; |
704 | 723 | ||
724 | down(&psmouse_sem); | ||
725 | |||
705 | /* | 726 | /* |
706 | * If this is a pass-through port deactivate parent so the device | 727 | * If this is a pass-through port deactivate parent so the device |
707 | * connected to this port can be successfully identified | 728 | * connected to this port can be successfully identified |
@@ -711,13 +732,11 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) | |||
711 | psmouse_deactivate(parent); | 732 | psmouse_deactivate(parent); |
712 | } | 733 | } |
713 | 734 | ||
714 | if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL))) { | 735 | if (!(psmouse = kcalloc(1, sizeof(struct psmouse), GFP_KERNEL))) { |
715 | retval = -ENOMEM; | 736 | retval = -ENOMEM; |
716 | goto out; | 737 | goto out; |
717 | } | 738 | } |
718 | 739 | ||
719 | memset(psmouse, 0, sizeof(struct psmouse)); | ||
720 | |||
721 | ps2_init(&psmouse->ps2dev, serio); | 740 | ps2_init(&psmouse->ps2dev, serio); |
722 | sprintf(psmouse->phys, "%s/input0", serio->phys); | 741 | sprintf(psmouse->phys, "%s/input0", serio->phys); |
723 | psmouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); | 742 | psmouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); |
@@ -785,10 +804,11 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) | |||
785 | retval = 0; | 804 | retval = 0; |
786 | 805 | ||
787 | out: | 806 | out: |
788 | /* If this is a pass-through port the parent awaits to be activated */ | 807 | /* If this is a pass-through port the parent needs to be re-activated */ |
789 | if (parent) | 808 | if (parent) |
790 | psmouse_activate(parent); | 809 | psmouse_activate(parent); |
791 | 810 | ||
811 | up(&psmouse_sem); | ||
792 | return retval; | 812 | return retval; |
793 | } | 813 | } |
794 | 814 | ||
@@ -805,6 +825,8 @@ static int psmouse_reconnect(struct serio *serio) | |||
805 | return -1; | 825 | return -1; |
806 | } | 826 | } |
807 | 827 | ||
828 | down(&psmouse_sem); | ||
829 | |||
808 | if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { | 830 | if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { |
809 | parent = serio_get_drvdata(serio->parent); | 831 | parent = serio_get_drvdata(serio->parent); |
810 | psmouse_deactivate(parent); | 832 | psmouse_deactivate(parent); |
@@ -837,6 +859,7 @@ out: | |||
837 | if (parent) | 859 | if (parent) |
838 | psmouse_activate(parent); | 860 | psmouse_activate(parent); |
839 | 861 | ||
862 | up(&psmouse_sem); | ||
840 | return rc; | 863 | return rc; |
841 | } | 864 | } |
842 | 865 | ||
@@ -907,7 +930,16 @@ ssize_t psmouse_attr_set_helper(struct device *dev, const char *buf, size_t coun | |||
907 | 930 | ||
908 | if (serio->drv != &psmouse_drv) { | 931 | if (serio->drv != &psmouse_drv) { |
909 | retval = -ENODEV; | 932 | retval = -ENODEV; |
910 | goto out; | 933 | goto out_unpin; |
934 | } | ||
935 | |||
936 | retval = down_interruptible(&psmouse_sem); | ||
937 | if (retval) | ||
938 | goto out_unpin; | ||
939 | |||
940 | if (psmouse->state == PSMOUSE_IGNORE) { | ||
941 | retval = -ENODEV; | ||
942 | goto out_up; | ||
911 | } | 943 | } |
912 | 944 | ||
913 | if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { | 945 | if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { |
@@ -922,7 +954,9 @@ ssize_t psmouse_attr_set_helper(struct device *dev, const char *buf, size_t coun | |||
922 | if (parent) | 954 | if (parent) |
923 | psmouse_activate(parent); | 955 | psmouse_activate(parent); |
924 | 956 | ||
925 | out: | 957 | out_up: |
958 | up(&psmouse_sem); | ||
959 | out_unpin: | ||
926 | serio_unpin_driver(serio); | 960 | serio_unpin_driver(serio); |
927 | return retval; | 961 | return retval; |
928 | } | 962 | } |