diff options
-rw-r--r-- | drivers/pcmcia/cs.c | 177 | ||||
-rw-r--r-- | drivers/pcmcia/cs_internal.h | 10 | ||||
-rw-r--r-- | drivers/pcmcia/pcmcia_ioctl.c | 8 | ||||
-rw-r--r-- | drivers/pcmcia/socket_sysfs.c | 26 | ||||
-rw-r--r-- | include/pcmcia/ss.h | 3 |
5 files changed, 83 insertions, 141 deletions
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 7ba45b0cca6b..823ecda32216 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c | |||
@@ -505,7 +505,10 @@ static int socket_insert(struct pcmcia_socket *skt) | |||
505 | dev_dbg(&skt->dev, "insert\n"); | 505 | dev_dbg(&skt->dev, "insert\n"); |
506 | 506 | ||
507 | mutex_lock(&skt->ops_mutex); | 507 | mutex_lock(&skt->ops_mutex); |
508 | WARN_ON(skt->state & SOCKET_INUSE); | 508 | if (skt->state & SOCKET_INUSE) { |
509 | mutex_unlock(&skt->ops_mutex); | ||
510 | return -EINVAL; | ||
511 | } | ||
509 | skt->state |= SOCKET_INUSE; | 512 | skt->state |= SOCKET_INUSE; |
510 | 513 | ||
511 | ret = socket_setup(skt, setup_delay); | 514 | ret = socket_setup(skt, setup_delay); |
@@ -682,16 +685,19 @@ static int pccardd(void *__skt) | |||
682 | for (;;) { | 685 | for (;;) { |
683 | unsigned long flags; | 686 | unsigned long flags; |
684 | unsigned int events; | 687 | unsigned int events; |
688 | unsigned int sysfs_events; | ||
685 | 689 | ||
686 | set_current_state(TASK_INTERRUPTIBLE); | 690 | set_current_state(TASK_INTERRUPTIBLE); |
687 | 691 | ||
688 | spin_lock_irqsave(&skt->thread_lock, flags); | 692 | spin_lock_irqsave(&skt->thread_lock, flags); |
689 | events = skt->thread_events; | 693 | events = skt->thread_events; |
690 | skt->thread_events = 0; | 694 | skt->thread_events = 0; |
695 | sysfs_events = skt->sysfs_events; | ||
696 | skt->sysfs_events = 0; | ||
691 | spin_unlock_irqrestore(&skt->thread_lock, flags); | 697 | spin_unlock_irqrestore(&skt->thread_lock, flags); |
692 | 698 | ||
699 | mutex_lock(&skt->skt_mutex); | ||
693 | if (events) { | 700 | if (events) { |
694 | mutex_lock(&skt->skt_mutex); | ||
695 | if (events & SS_DETECT) | 701 | if (events & SS_DETECT) |
696 | socket_detect_change(skt); | 702 | socket_detect_change(skt); |
697 | if (events & SS_BATDEAD) | 703 | if (events & SS_BATDEAD) |
@@ -700,10 +706,34 @@ static int pccardd(void *__skt) | |||
700 | send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW); | 706 | send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW); |
701 | if (events & SS_READY) | 707 | if (events & SS_READY) |
702 | send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW); | 708 | send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW); |
703 | mutex_unlock(&skt->skt_mutex); | ||
704 | continue; | ||
705 | } | 709 | } |
706 | 710 | ||
711 | if (sysfs_events) { | ||
712 | if (sysfs_events & PCMCIA_UEVENT_EJECT) | ||
713 | socket_remove(skt); | ||
714 | if (sysfs_events & PCMCIA_UEVENT_INSERT) | ||
715 | socket_insert(skt); | ||
716 | if ((sysfs_events & PCMCIA_UEVENT_RESUME) && | ||
717 | !(skt->state & SOCKET_CARDBUS)) { | ||
718 | ret = socket_resume(skt); | ||
719 | if (!ret && skt->callback) | ||
720 | skt->callback->resume(skt); | ||
721 | } | ||
722 | if ((sysfs_events & PCMCIA_UEVENT_SUSPEND) && | ||
723 | !(skt->state & SOCKET_CARDBUS)) { | ||
724 | if (skt->callback) | ||
725 | ret = skt->callback->suspend(skt); | ||
726 | else | ||
727 | ret = 0; | ||
728 | if (!ret) | ||
729 | socket_suspend(skt); | ||
730 | } | ||
731 | } | ||
732 | mutex_unlock(&skt->skt_mutex); | ||
733 | |||
734 | if (events || sysfs_events) | ||
735 | continue; | ||
736 | |||
707 | if (kthread_should_stop()) | 737 | if (kthread_should_stop()) |
708 | break; | 738 | break; |
709 | 739 | ||
@@ -745,6 +775,30 @@ void pcmcia_parse_events(struct pcmcia_socket *s, u_int events) | |||
745 | } /* pcmcia_parse_events */ | 775 | } /* pcmcia_parse_events */ |
746 | EXPORT_SYMBOL(pcmcia_parse_events); | 776 | EXPORT_SYMBOL(pcmcia_parse_events); |
747 | 777 | ||
778 | /** | ||
779 | * pcmcia_parse_uevents() - tell pccardd to issue manual commands | ||
780 | * @s: the PCMCIA socket we wan't to command | ||
781 | * @events: events to pass to pccardd | ||
782 | * | ||
783 | * userspace-issued insert, eject, suspend and resume commands must be | ||
784 | * handled by pccardd to avoid any sysfs-related deadlocks. Valid events | ||
785 | * are PCMCIA_UEVENT_EJECT (for eject), PCMCIA_UEVENT__INSERT (for insert), | ||
786 | * PCMCIA_UEVENT_RESUME (for resume) and PCMCIA_UEVENT_SUSPEND (for suspend). | ||
787 | */ | ||
788 | void pcmcia_parse_uevents(struct pcmcia_socket *s, u_int events) | ||
789 | { | ||
790 | unsigned long flags; | ||
791 | dev_dbg(&s->dev, "parse_uevents: events %08x\n", events); | ||
792 | if (s->thread) { | ||
793 | spin_lock_irqsave(&s->thread_lock, flags); | ||
794 | s->sysfs_events |= events; | ||
795 | spin_unlock_irqrestore(&s->thread_lock, flags); | ||
796 | |||
797 | wake_up_process(s->thread); | ||
798 | } | ||
799 | } | ||
800 | EXPORT_SYMBOL(pcmcia_parse_uevents); | ||
801 | |||
748 | 802 | ||
749 | /* register pcmcia_callback */ | 803 | /* register pcmcia_callback */ |
750 | int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) | 804 | int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) |
@@ -828,121 +882,6 @@ int pcmcia_reset_card(struct pcmcia_socket *skt) | |||
828 | EXPORT_SYMBOL(pcmcia_reset_card); | 882 | EXPORT_SYMBOL(pcmcia_reset_card); |
829 | 883 | ||
830 | 884 | ||
831 | /* These shut down or wake up a socket. They are sort of user | ||
832 | * initiated versions of the APM suspend and resume actions. | ||
833 | */ | ||
834 | int pcmcia_suspend_card(struct pcmcia_socket *skt) | ||
835 | { | ||
836 | int ret; | ||
837 | |||
838 | dev_dbg(&skt->dev, "suspending socket\n"); | ||
839 | |||
840 | mutex_lock(&skt->skt_mutex); | ||
841 | do { | ||
842 | if (!(skt->state & SOCKET_PRESENT)) { | ||
843 | ret = -ENODEV; | ||
844 | break; | ||
845 | } | ||
846 | if (skt->state & SOCKET_CARDBUS) { | ||
847 | ret = -EPERM; | ||
848 | break; | ||
849 | } | ||
850 | if (skt->callback) { | ||
851 | ret = skt->callback->suspend(skt); | ||
852 | if (ret) | ||
853 | break; | ||
854 | } | ||
855 | ret = socket_suspend(skt); | ||
856 | } while (0); | ||
857 | mutex_unlock(&skt->skt_mutex); | ||
858 | |||
859 | return ret; | ||
860 | } /* suspend_card */ | ||
861 | EXPORT_SYMBOL(pcmcia_suspend_card); | ||
862 | |||
863 | |||
864 | int pcmcia_resume_card(struct pcmcia_socket *skt) | ||
865 | { | ||
866 | int ret; | ||
867 | |||
868 | dev_dbg(&skt->dev, "waking up socket\n"); | ||
869 | |||
870 | mutex_lock(&skt->skt_mutex); | ||
871 | do { | ||
872 | if (!(skt->state & SOCKET_PRESENT)) { | ||
873 | ret = -ENODEV; | ||
874 | break; | ||
875 | } | ||
876 | if (skt->state & SOCKET_CARDBUS) { | ||
877 | ret = -EPERM; | ||
878 | break; | ||
879 | } | ||
880 | ret = socket_resume(skt); | ||
881 | if (!ret && skt->callback) | ||
882 | skt->callback->resume(skt); | ||
883 | } while (0); | ||
884 | mutex_unlock(&skt->skt_mutex); | ||
885 | |||
886 | return ret; | ||
887 | } /* resume_card */ | ||
888 | EXPORT_SYMBOL(pcmcia_resume_card); | ||
889 | |||
890 | |||
891 | /* These handle user requests to eject or insert a card. */ | ||
892 | int pcmcia_eject_card(struct pcmcia_socket *skt) | ||
893 | { | ||
894 | int ret; | ||
895 | |||
896 | dev_dbg(&skt->dev, "user eject request\n"); | ||
897 | |||
898 | mutex_lock(&skt->skt_mutex); | ||
899 | do { | ||
900 | if (!(skt->state & SOCKET_PRESENT)) { | ||
901 | ret = -ENODEV; | ||
902 | break; | ||
903 | } | ||
904 | |||
905 | ret = send_event(skt, CS_EVENT_EJECTION_REQUEST, CS_EVENT_PRI_LOW); | ||
906 | if (ret != 0) { | ||
907 | ret = -EINVAL; | ||
908 | break; | ||
909 | } | ||
910 | |||
911 | socket_remove(skt); | ||
912 | ret = 0; | ||
913 | } while (0); | ||
914 | mutex_unlock(&skt->skt_mutex); | ||
915 | |||
916 | return ret; | ||
917 | } /* eject_card */ | ||
918 | EXPORT_SYMBOL(pcmcia_eject_card); | ||
919 | |||
920 | |||
921 | int pcmcia_insert_card(struct pcmcia_socket *skt) | ||
922 | { | ||
923 | int ret; | ||
924 | |||
925 | dev_dbg(&skt->dev, "user insert request\n"); | ||
926 | |||
927 | mutex_lock(&skt->skt_mutex); | ||
928 | do { | ||
929 | if (skt->state & SOCKET_PRESENT) { | ||
930 | ret = -EBUSY; | ||
931 | break; | ||
932 | } | ||
933 | if (socket_insert(skt) == -ENODEV) { | ||
934 | ret = -ENODEV; | ||
935 | break; | ||
936 | } | ||
937 | ret = 0; | ||
938 | } while (0); | ||
939 | mutex_unlock(&skt->skt_mutex); | ||
940 | |||
941 | return ret; | ||
942 | } /* insert_card */ | ||
943 | EXPORT_SYMBOL(pcmcia_insert_card); | ||
944 | |||
945 | |||
946 | static int pcmcia_socket_uevent(struct device *dev, | 885 | static int pcmcia_socket_uevent(struct device *dev, |
947 | struct kobj_uevent_env *env) | 886 | struct kobj_uevent_env *env) |
948 | { | 887 | { |
diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index bd386d77845e..127c97acf849 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h | |||
@@ -124,11 +124,11 @@ extern struct class pcmcia_socket_class; | |||
124 | int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c); | 124 | int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c); |
125 | struct pcmcia_socket *pcmcia_get_socket_by_nr(unsigned int nr); | 125 | struct pcmcia_socket *pcmcia_get_socket_by_nr(unsigned int nr); |
126 | 126 | ||
127 | int pcmcia_suspend_card(struct pcmcia_socket *skt); | 127 | void pcmcia_parse_uevents(struct pcmcia_socket *socket, unsigned int events); |
128 | int pcmcia_resume_card(struct pcmcia_socket *skt); | 128 | #define PCMCIA_UEVENT_EJECT 0x0001 |
129 | 129 | #define PCMCIA_UEVENT_INSERT 0x0002 | |
130 | int pcmcia_eject_card(struct pcmcia_socket *skt); | 130 | #define PCMCIA_UEVENT_SUSPEND 0x0004 |
131 | int pcmcia_insert_card(struct pcmcia_socket *skt); | 131 | #define PCMCIA_UEVENT_RESUME 0x0008 |
132 | 132 | ||
133 | struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt); | 133 | struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt); |
134 | void pcmcia_put_socket(struct pcmcia_socket *skt); | 134 | void pcmcia_put_socket(struct pcmcia_socket *skt); |
diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c index 96fd236f52a9..13a7132cf688 100644 --- a/drivers/pcmcia/pcmcia_ioctl.c +++ b/drivers/pcmcia/pcmcia_ioctl.c | |||
@@ -925,16 +925,16 @@ static int ds_ioctl(struct inode *inode, struct file *file, | |||
925 | ret = pccard_validate_cis(s, &buf->cisinfo.Chains); | 925 | ret = pccard_validate_cis(s, &buf->cisinfo.Chains); |
926 | break; | 926 | break; |
927 | case DS_SUSPEND_CARD: | 927 | case DS_SUSPEND_CARD: |
928 | ret = pcmcia_suspend_card(s); | 928 | pcmcia_parse_uevents(s, PCMCIA_UEVENT_SUSPEND); |
929 | break; | 929 | break; |
930 | case DS_RESUME_CARD: | 930 | case DS_RESUME_CARD: |
931 | ret = pcmcia_resume_card(s); | 931 | pcmcia_parse_uevents(s, PCMCIA_UEVENT_RESUME); |
932 | break; | 932 | break; |
933 | case DS_EJECT_CARD: | 933 | case DS_EJECT_CARD: |
934 | err = pcmcia_eject_card(s); | 934 | pcmcia_parse_uevents(s, PCMCIA_UEVENT_EJECT); |
935 | break; | 935 | break; |
936 | case DS_INSERT_CARD: | 936 | case DS_INSERT_CARD: |
937 | err = pcmcia_insert_card(s); | 937 | pcmcia_parse_uevents(s, PCMCIA_UEVENT_INSERT); |
938 | break; | 938 | break; |
939 | case DS_ACCESS_CONFIGURATION_REGISTER: | 939 | case DS_ACCESS_CONFIGURATION_REGISTER: |
940 | if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) { | 940 | if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) { |
diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c index e8826df00a36..fba0e30183f4 100644 --- a/drivers/pcmcia/socket_sysfs.c +++ b/drivers/pcmcia/socket_sysfs.c | |||
@@ -88,15 +88,14 @@ static DEVICE_ATTR(card_vcc, 0444, pccard_show_vcc, NULL); | |||
88 | static ssize_t pccard_store_insert(struct device *dev, struct device_attribute *attr, | 88 | static ssize_t pccard_store_insert(struct device *dev, struct device_attribute *attr, |
89 | const char *buf, size_t count) | 89 | const char *buf, size_t count) |
90 | { | 90 | { |
91 | ssize_t ret; | ||
92 | struct pcmcia_socket *s = to_socket(dev); | 91 | struct pcmcia_socket *s = to_socket(dev); |
93 | 92 | ||
94 | if (!count) | 93 | if (!count) |
95 | return -EINVAL; | 94 | return -EINVAL; |
96 | 95 | ||
97 | ret = pcmcia_insert_card(s); | 96 | pcmcia_parse_uevents(s, PCMCIA_UEVENT_INSERT); |
98 | 97 | ||
99 | return ret ? ret : count; | 98 | return count; |
100 | } | 99 | } |
101 | static DEVICE_ATTR(card_insert, 0200, NULL, pccard_store_insert); | 100 | static DEVICE_ATTR(card_insert, 0200, NULL, pccard_store_insert); |
102 | 101 | ||
@@ -113,18 +112,22 @@ static ssize_t pccard_store_card_pm_state(struct device *dev, | |||
113 | struct device_attribute *attr, | 112 | struct device_attribute *attr, |
114 | const char *buf, size_t count) | 113 | const char *buf, size_t count) |
115 | { | 114 | { |
116 | ssize_t ret = -EINVAL; | ||
117 | struct pcmcia_socket *s = to_socket(dev); | 115 | struct pcmcia_socket *s = to_socket(dev); |
116 | ssize_t ret = count; | ||
118 | 117 | ||
119 | if (!count) | 118 | if (!count) |
120 | return -EINVAL; | 119 | return -EINVAL; |
121 | 120 | ||
122 | if (!(s->state & SOCKET_SUSPEND) && !strncmp(buf, "off", 3)) | 121 | if (!strncmp(buf, "off", 3)) |
123 | ret = pcmcia_suspend_card(s); | 122 | pcmcia_parse_uevents(s, PCMCIA_UEVENT_SUSPEND); |
124 | else if ((s->state & SOCKET_SUSPEND) && !strncmp(buf, "on", 2)) | 123 | else { |
125 | ret = pcmcia_resume_card(s); | 124 | if (!strncmp(buf, "on", 2)) |
125 | pcmcia_parse_uevents(s, PCMCIA_UEVENT_RESUME); | ||
126 | else | ||
127 | ret = -EINVAL; | ||
128 | } | ||
126 | 129 | ||
127 | return ret ? -ENODEV : count; | 130 | return ret; |
128 | } | 131 | } |
129 | static DEVICE_ATTR(card_pm_state, 0644, pccard_show_card_pm_state, pccard_store_card_pm_state); | 132 | static DEVICE_ATTR(card_pm_state, 0644, pccard_show_card_pm_state, pccard_store_card_pm_state); |
130 | 133 | ||
@@ -132,15 +135,14 @@ static ssize_t pccard_store_eject(struct device *dev, | |||
132 | struct device_attribute *attr, | 135 | struct device_attribute *attr, |
133 | const char *buf, size_t count) | 136 | const char *buf, size_t count) |
134 | { | 137 | { |
135 | ssize_t ret; | ||
136 | struct pcmcia_socket *s = to_socket(dev); | 138 | struct pcmcia_socket *s = to_socket(dev); |
137 | 139 | ||
138 | if (!count) | 140 | if (!count) |
139 | return -EINVAL; | 141 | return -EINVAL; |
140 | 142 | ||
141 | ret = pcmcia_eject_card(s); | 143 | pcmcia_parse_uevents(s, PCMCIA_UEVENT_EJECT); |
142 | 144 | ||
143 | return ret ? ret : count; | 145 | return count; |
144 | } | 146 | } |
145 | static DEVICE_ATTR(card_eject, 0200, NULL, pccard_store_eject); | 147 | static DEVICE_ATTR(card_eject, 0200, NULL, pccard_store_eject); |
146 | 148 | ||
diff --git a/include/pcmcia/ss.h b/include/pcmcia/ss.h index cfaccc224b50..ea5dec8c0d76 100644 --- a/include/pcmcia/ss.h +++ b/include/pcmcia/ss.h | |||
@@ -200,13 +200,14 @@ struct pcmcia_socket { | |||
200 | struct task_struct *thread; | 200 | struct task_struct *thread; |
201 | struct completion thread_done; | 201 | struct completion thread_done; |
202 | unsigned int thread_events; | 202 | unsigned int thread_events; |
203 | unsigned int sysfs_events; | ||
203 | 204 | ||
204 | /* For the non-trivial interaction between these locks, | 205 | /* For the non-trivial interaction between these locks, |
205 | * see Documentation/pcmcia/locking.txt */ | 206 | * see Documentation/pcmcia/locking.txt */ |
206 | struct mutex skt_mutex; | 207 | struct mutex skt_mutex; |
207 | struct mutex ops_mutex; | 208 | struct mutex ops_mutex; |
208 | 209 | ||
209 | /* protects thread_events */ | 210 | /* protects thread_events and sysfs_events */ |
210 | spinlock_t thread_lock; | 211 | spinlock_t thread_lock; |
211 | 212 | ||
212 | /* pcmcia (16-bit) */ | 213 | /* pcmcia (16-bit) */ |