diff options
author | Dmitry Torokhov <dtor_core@ameritech.net> | 2005-06-01 03:39:44 -0400 |
---|---|---|
committer | Dmitry Torokhov <dtor_core@ameritech.net> | 2005-06-01 03:39:44 -0400 |
commit | 04df1925fcda9a35c716423ad2b73abd70eb0913 (patch) | |
tree | 6094c0e71b5d17a62211cd56da7bb0ecb97a3df6 | |
parent | 8121152c1770ef1cd029030d51802c65c489950d (diff) |
Input: pmouse - introduce proper locking so state-changing
operations do not iterfere with each other.
Also make sure that serio core takes serio->drv_sem
not only for connect/disconnect but for reconnect
too.
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
-rw-r--r-- | drivers/input/mouse/psmouse-base.c | 54 | ||||
-rw-r--r-- | drivers/input/serio/serio.c | 44 |
2 files changed, 80 insertions, 18 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 | } |
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 2c93ceab831a..b82815a0b65b 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c | |||
@@ -67,6 +67,37 @@ static void serio_destroy_port(struct serio *serio); | |||
67 | static void serio_reconnect_port(struct serio *serio); | 67 | static void serio_reconnect_port(struct serio *serio); |
68 | static void serio_disconnect_port(struct serio *serio); | 68 | static void serio_disconnect_port(struct serio *serio); |
69 | 69 | ||
70 | static int serio_connect_driver(struct serio *serio, struct serio_driver *drv) | ||
71 | { | ||
72 | int retval; | ||
73 | |||
74 | down(&serio->drv_sem); | ||
75 | retval = drv->connect(serio, drv); | ||
76 | up(&serio->drv_sem); | ||
77 | |||
78 | return retval; | ||
79 | } | ||
80 | |||
81 | static int serio_reconnect_driver(struct serio *serio) | ||
82 | { | ||
83 | int retval = -1; | ||
84 | |||
85 | down(&serio->drv_sem); | ||
86 | if (serio->drv && serio->drv->reconnect) | ||
87 | retval = serio->drv->reconnect(serio); | ||
88 | up(&serio->drv_sem); | ||
89 | |||
90 | return retval; | ||
91 | } | ||
92 | |||
93 | static void serio_disconnect_driver(struct serio *serio) | ||
94 | { | ||
95 | down(&serio->drv_sem); | ||
96 | if (serio->drv) | ||
97 | serio->drv->disconnect(serio); | ||
98 | up(&serio->drv_sem); | ||
99 | } | ||
100 | |||
70 | static int serio_match_port(const struct serio_device_id *ids, struct serio *serio) | 101 | static int serio_match_port(const struct serio_device_id *ids, struct serio *serio) |
71 | { | 102 | { |
72 | while (ids->type || ids->proto) { | 103 | while (ids->type || ids->proto) { |
@@ -90,7 +121,7 @@ static void serio_bind_driver(struct serio *serio, struct serio_driver *drv) | |||
90 | 121 | ||
91 | if (serio_match_port(drv->id_table, serio)) { | 122 | if (serio_match_port(drv->id_table, serio)) { |
92 | serio->dev.driver = &drv->driver; | 123 | serio->dev.driver = &drv->driver; |
93 | if (drv->connect(serio, drv)) { | 124 | if (serio_connect_driver(serio, drv)) { |
94 | serio->dev.driver = NULL; | 125 | serio->dev.driver = NULL; |
95 | goto out; | 126 | goto out; |
96 | } | 127 | } |
@@ -550,7 +581,7 @@ static void serio_destroy_port(struct serio *serio) | |||
550 | static void serio_reconnect_port(struct serio *serio) | 581 | static void serio_reconnect_port(struct serio *serio) |
551 | { | 582 | { |
552 | do { | 583 | do { |
553 | if (!serio->drv || !serio->drv->reconnect || serio->drv->reconnect(serio)) { | 584 | if (serio_reconnect_driver(serio)) { |
554 | serio_disconnect_port(serio); | 585 | serio_disconnect_port(serio); |
555 | serio_find_driver(serio); | 586 | serio_find_driver(serio); |
556 | /* Ok, old children are now gone, we are done */ | 587 | /* Ok, old children are now gone, we are done */ |
@@ -679,15 +710,14 @@ static int serio_driver_probe(struct device *dev) | |||
679 | struct serio *serio = to_serio_port(dev); | 710 | struct serio *serio = to_serio_port(dev); |
680 | struct serio_driver *drv = to_serio_driver(dev->driver); | 711 | struct serio_driver *drv = to_serio_driver(dev->driver); |
681 | 712 | ||
682 | return drv->connect(serio, drv); | 713 | return serio_connect_driver(serio, drv); |
683 | } | 714 | } |
684 | 715 | ||
685 | static int serio_driver_remove(struct device *dev) | 716 | static int serio_driver_remove(struct device *dev) |
686 | { | 717 | { |
687 | struct serio *serio = to_serio_port(dev); | 718 | struct serio *serio = to_serio_port(dev); |
688 | struct serio_driver *drv = to_serio_driver(dev->driver); | ||
689 | 719 | ||
690 | drv->disconnect(serio); | 720 | serio_disconnect_driver(serio); |
691 | return 0; | 721 | return 0; |
692 | } | 722 | } |
693 | 723 | ||
@@ -723,11 +753,9 @@ start_over: | |||
723 | 753 | ||
724 | static void serio_set_drv(struct serio *serio, struct serio_driver *drv) | 754 | static void serio_set_drv(struct serio *serio, struct serio_driver *drv) |
725 | { | 755 | { |
726 | down(&serio->drv_sem); | ||
727 | serio_pause_rx(serio); | 756 | serio_pause_rx(serio); |
728 | serio->drv = drv; | 757 | serio->drv = drv; |
729 | serio_continue_rx(serio); | 758 | serio_continue_rx(serio); |
730 | up(&serio->drv_sem); | ||
731 | } | 759 | } |
732 | 760 | ||
733 | static int serio_bus_match(struct device *dev, struct device_driver *drv) | 761 | static int serio_bus_match(struct device *dev, struct device_driver *drv) |
@@ -787,7 +815,7 @@ static int serio_resume(struct device *dev) | |||
787 | { | 815 | { |
788 | struct serio *serio = to_serio_port(dev); | 816 | struct serio *serio = to_serio_port(dev); |
789 | 817 | ||
790 | if (!serio->drv || !serio->drv->reconnect || serio->drv->reconnect(serio)) { | 818 | if (serio_reconnect_driver(serio)) { |
791 | /* | 819 | /* |
792 | * Driver re-probing can take a while, so better let kseriod | 820 | * Driver re-probing can take a while, so better let kseriod |
793 | * deal with it. | 821 | * deal with it. |