|
@@ -505,7 +505,10 @@ static int socket_insert(struct pcmcia_socket *skt)
|
|
dev_dbg(&skt->dev, "insert\n");
|
|
dev_dbg(&skt->dev, "insert\n");
|
|
|
|
|
|
mutex_lock(&skt->ops_mutex);
|
|
mutex_lock(&skt->ops_mutex);
|
|
- WARN_ON(skt->state & SOCKET_INUSE);
|
|
|
|
|
|
+ if (skt->state & SOCKET_INUSE) {
|
|
|
|
+ mutex_unlock(&skt->ops_mutex);
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
skt->state |= SOCKET_INUSE;
|
|
skt->state |= SOCKET_INUSE;
|
|
|
|
|
|
ret = socket_setup(skt, setup_delay);
|
|
ret = socket_setup(skt, setup_delay);
|
|
@@ -682,16 +685,19 @@ static int pccardd(void *__skt)
|
|
for (;;) {
|
|
for (;;) {
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
unsigned int events;
|
|
unsigned int events;
|
|
|
|
+ unsigned int sysfs_events;
|
|
|
|
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
|
|
|
|
|
spin_lock_irqsave(&skt->thread_lock, flags);
|
|
spin_lock_irqsave(&skt->thread_lock, flags);
|
|
events = skt->thread_events;
|
|
events = skt->thread_events;
|
|
skt->thread_events = 0;
|
|
skt->thread_events = 0;
|
|
|
|
+ sysfs_events = skt->sysfs_events;
|
|
|
|
+ skt->sysfs_events = 0;
|
|
spin_unlock_irqrestore(&skt->thread_lock, flags);
|
|
spin_unlock_irqrestore(&skt->thread_lock, flags);
|
|
|
|
|
|
|
|
+ mutex_lock(&skt->skt_mutex);
|
|
if (events) {
|
|
if (events) {
|
|
- mutex_lock(&skt->skt_mutex);
|
|
|
|
if (events & SS_DETECT)
|
|
if (events & SS_DETECT)
|
|
socket_detect_change(skt);
|
|
socket_detect_change(skt);
|
|
if (events & SS_BATDEAD)
|
|
if (events & SS_BATDEAD)
|
|
@@ -700,10 +706,34 @@ static int pccardd(void *__skt)
|
|
send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW);
|
|
send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW);
|
|
if (events & SS_READY)
|
|
if (events & SS_READY)
|
|
send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW);
|
|
send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW);
|
|
- mutex_unlock(&skt->skt_mutex);
|
|
|
|
- continue;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (sysfs_events) {
|
|
|
|
+ if (sysfs_events & PCMCIA_UEVENT_EJECT)
|
|
|
|
+ socket_remove(skt);
|
|
|
|
+ if (sysfs_events & PCMCIA_UEVENT_INSERT)
|
|
|
|
+ socket_insert(skt);
|
|
|
|
+ if ((sysfs_events & PCMCIA_UEVENT_RESUME) &&
|
|
|
|
+ !(skt->state & SOCKET_CARDBUS)) {
|
|
|
|
+ ret = socket_resume(skt);
|
|
|
|
+ if (!ret && skt->callback)
|
|
|
|
+ skt->callback->resume(skt);
|
|
|
|
+ }
|
|
|
|
+ if ((sysfs_events & PCMCIA_UEVENT_SUSPEND) &&
|
|
|
|
+ !(skt->state & SOCKET_CARDBUS)) {
|
|
|
|
+ if (skt->callback)
|
|
|
|
+ ret = skt->callback->suspend(skt);
|
|
|
|
+ else
|
|
|
|
+ ret = 0;
|
|
|
|
+ if (!ret)
|
|
|
|
+ socket_suspend(skt);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ mutex_unlock(&skt->skt_mutex);
|
|
|
|
+
|
|
|
|
+ if (events || sysfs_events)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
if (kthread_should_stop())
|
|
if (kthread_should_stop())
|
|
break;
|
|
break;
|
|
|
|
|
|
@@ -745,6 +775,30 @@ void pcmcia_parse_events(struct pcmcia_socket *s, u_int events)
|
|
} /* pcmcia_parse_events */
|
|
} /* pcmcia_parse_events */
|
|
EXPORT_SYMBOL(pcmcia_parse_events);
|
|
EXPORT_SYMBOL(pcmcia_parse_events);
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * pcmcia_parse_uevents() - tell pccardd to issue manual commands
|
|
|
|
+ * @s: the PCMCIA socket we wan't to command
|
|
|
|
+ * @events: events to pass to pccardd
|
|
|
|
+ *
|
|
|
|
+ * userspace-issued insert, eject, suspend and resume commands must be
|
|
|
|
+ * handled by pccardd to avoid any sysfs-related deadlocks. Valid events
|
|
|
|
+ * are PCMCIA_UEVENT_EJECT (for eject), PCMCIA_UEVENT__INSERT (for insert),
|
|
|
|
+ * PCMCIA_UEVENT_RESUME (for resume) and PCMCIA_UEVENT_SUSPEND (for suspend).
|
|
|
|
+ */
|
|
|
|
+void pcmcia_parse_uevents(struct pcmcia_socket *s, u_int events)
|
|
|
|
+{
|
|
|
|
+ unsigned long flags;
|
|
|
|
+ dev_dbg(&s->dev, "parse_uevents: events %08x\n", events);
|
|
|
|
+ if (s->thread) {
|
|
|
|
+ spin_lock_irqsave(&s->thread_lock, flags);
|
|
|
|
+ s->sysfs_events |= events;
|
|
|
|
+ spin_unlock_irqrestore(&s->thread_lock, flags);
|
|
|
|
+
|
|
|
|
+ wake_up_process(s->thread);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(pcmcia_parse_uevents);
|
|
|
|
+
|
|
|
|
|
|
/* register pcmcia_callback */
|
|
/* register pcmcia_callback */
|
|
int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c)
|
|
int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c)
|
|
@@ -828,121 +882,6 @@ int pcmcia_reset_card(struct pcmcia_socket *skt)
|
|
EXPORT_SYMBOL(pcmcia_reset_card);
|
|
EXPORT_SYMBOL(pcmcia_reset_card);
|
|
|
|
|
|
|
|
|
|
-/* These shut down or wake up a socket. They are sort of user
|
|
|
|
- * initiated versions of the APM suspend and resume actions.
|
|
|
|
- */
|
|
|
|
-int pcmcia_suspend_card(struct pcmcia_socket *skt)
|
|
|
|
-{
|
|
|
|
- int ret;
|
|
|
|
-
|
|
|
|
- dev_dbg(&skt->dev, "suspending socket\n");
|
|
|
|
-
|
|
|
|
- mutex_lock(&skt->skt_mutex);
|
|
|
|
- do {
|
|
|
|
- if (!(skt->state & SOCKET_PRESENT)) {
|
|
|
|
- ret = -ENODEV;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- if (skt->state & SOCKET_CARDBUS) {
|
|
|
|
- ret = -EPERM;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- if (skt->callback) {
|
|
|
|
- ret = skt->callback->suspend(skt);
|
|
|
|
- if (ret)
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- ret = socket_suspend(skt);
|
|
|
|
- } while (0);
|
|
|
|
- mutex_unlock(&skt->skt_mutex);
|
|
|
|
-
|
|
|
|
- return ret;
|
|
|
|
-} /* suspend_card */
|
|
|
|
-EXPORT_SYMBOL(pcmcia_suspend_card);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-int pcmcia_resume_card(struct pcmcia_socket *skt)
|
|
|
|
-{
|
|
|
|
- int ret;
|
|
|
|
-
|
|
|
|
- dev_dbg(&skt->dev, "waking up socket\n");
|
|
|
|
-
|
|
|
|
- mutex_lock(&skt->skt_mutex);
|
|
|
|
- do {
|
|
|
|
- if (!(skt->state & SOCKET_PRESENT)) {
|
|
|
|
- ret = -ENODEV;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- if (skt->state & SOCKET_CARDBUS) {
|
|
|
|
- ret = -EPERM;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- ret = socket_resume(skt);
|
|
|
|
- if (!ret && skt->callback)
|
|
|
|
- skt->callback->resume(skt);
|
|
|
|
- } while (0);
|
|
|
|
- mutex_unlock(&skt->skt_mutex);
|
|
|
|
-
|
|
|
|
- return ret;
|
|
|
|
-} /* resume_card */
|
|
|
|
-EXPORT_SYMBOL(pcmcia_resume_card);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-/* These handle user requests to eject or insert a card. */
|
|
|
|
-int pcmcia_eject_card(struct pcmcia_socket *skt)
|
|
|
|
-{
|
|
|
|
- int ret;
|
|
|
|
-
|
|
|
|
- dev_dbg(&skt->dev, "user eject request\n");
|
|
|
|
-
|
|
|
|
- mutex_lock(&skt->skt_mutex);
|
|
|
|
- do {
|
|
|
|
- if (!(skt->state & SOCKET_PRESENT)) {
|
|
|
|
- ret = -ENODEV;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- ret = send_event(skt, CS_EVENT_EJECTION_REQUEST, CS_EVENT_PRI_LOW);
|
|
|
|
- if (ret != 0) {
|
|
|
|
- ret = -EINVAL;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- socket_remove(skt);
|
|
|
|
- ret = 0;
|
|
|
|
- } while (0);
|
|
|
|
- mutex_unlock(&skt->skt_mutex);
|
|
|
|
-
|
|
|
|
- return ret;
|
|
|
|
-} /* eject_card */
|
|
|
|
-EXPORT_SYMBOL(pcmcia_eject_card);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-int pcmcia_insert_card(struct pcmcia_socket *skt)
|
|
|
|
-{
|
|
|
|
- int ret;
|
|
|
|
-
|
|
|
|
- dev_dbg(&skt->dev, "user insert request\n");
|
|
|
|
-
|
|
|
|
- mutex_lock(&skt->skt_mutex);
|
|
|
|
- do {
|
|
|
|
- if (skt->state & SOCKET_PRESENT) {
|
|
|
|
- ret = -EBUSY;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- if (socket_insert(skt) == -ENODEV) {
|
|
|
|
- ret = -ENODEV;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- ret = 0;
|
|
|
|
- } while (0);
|
|
|
|
- mutex_unlock(&skt->skt_mutex);
|
|
|
|
-
|
|
|
|
- return ret;
|
|
|
|
-} /* insert_card */
|
|
|
|
-EXPORT_SYMBOL(pcmcia_insert_card);
|
|
|
|
-
|
|
|
|
-
|
|
|
|
static int pcmcia_socket_uevent(struct device *dev,
|
|
static int pcmcia_socket_uevent(struct device *dev,
|
|
struct kobj_uevent_env *env)
|
|
struct kobj_uevent_env *env)
|
|
{
|
|
{
|