Re: PCMCIA 2.5.X sleeping from illegal context

From: Russell King (rmk@arm.linux.org.uk)
Date: Mon May 12 2003 - 17:48:28 EST


On Mon, May 12, 2003 at 07:36:05AM -0500, Paul Fulghum wrote:
> On Mon, 2003-05-12 at 16:07, Alan Cox wrote:
> > On Llu, 2003-05-12 at 22:35, Paul Fulghum wrote:
> > > The 2.5.X PCMCIA kernel support seems to have a problem
> > > with drivers/pcmcia/rsrc_mgr.c in function undo_irq().
> >
> > Does this still happen with all the patches Russell King posted
> > that everyone else is ignoring ?
>
> I don't know, I've been ignoring them :-)
>
> Seriously, are they centralized someplace or
> should I scan back and try to extract them
> from the lk archive? Do you know about when
> they were posted?

Try this one. Its a slightly updated version from the one I sent earlier.

diff -u /net/flint/usrc/linux-bk-2.5/linux-2.5-pcmcia/drivers/pcmcia/cs.c linux/drivers/pcmcia/cs.c
--- /net/flint/usrc/linux-bk-2.5/linux-2.5-pcmcia/drivers/pcmcia/cs.c Sat Apr 26 23:00:59 2003
+++ linux/drivers/pcmcia/cs.c Sat Apr 26 23:07:24 2003
@@ -302,11 +302,8 @@
     
 ======================================================================*/
 
-static int setup_socket(socket_info_t *);
-static void shutdown_socket(socket_info_t *);
-static void reset_socket(socket_info_t *);
-static void unreset_socket(socket_info_t *);
-static void parse_events(void *info, u_int events);
+static int pccardd(void *__skt);
+void pcmcia_unregister_socket(struct class_device *dev);
 
 #define to_class_data(dev) dev->class_data
 
@@ -317,7 +314,7 @@
 {
         struct pcmcia_socket_class_data *cls_d = class_get_devdata(class_dev);
         socket_info_t *s_info;
- unsigned int i, j;
+ unsigned int i, j, ret;
 
         if (!cls_d)
                 return -EINVAL;
@@ -330,6 +327,7 @@
         memset(s_info, 0, cls_d->nsock * sizeof(socket_info_t));
 
         cls_d->s_info = s_info;
+ ret = 0;
 
         /* socket initialization */
         for (i = 0; i < cls_d->nsock; i++) {
@@ -344,7 +342,7 @@
                 s->erase_busy.next = s->erase_busy.prev = &s->erase_busy;
                 INIT_LIST_HEAD(&s->cis_cache);
                 spin_lock_init(&s->lock);
-
+
                 /* TBD: remove usage of socket_table, use class_for_each_dev instead */
                 for (j = 0; j < sockets; j++)
                         if (socket_table[j] == NULL) break;
@@ -353,6 +351,20 @@
 
                 init_socket(s);
                 s->ss_entry->inquire_socket(s->sock, &s->cap);
+
+ init_completion(&s->thread_done);
+ init_waitqueue_head(&s->thread_wait);
+ init_MUTEX(&s->skt_sem);
+ spin_lock_init(&s->thread_lock);
+ ret = kernel_thread(pccardd, s, CLONE_KERNEL);
+ if (ret < 0) {
+ pcmcia_unregister_socket(class_dev);
+ break;
+ }
+
+ wait_for_completion(&s->thread_done);
+ BUG_ON(!s->thread);
+
 #ifdef CONFIG_PROC_FS
                 if (proc_pccard) {
                         char name[3];
@@ -368,7 +380,7 @@
                 }
 #endif
         }
- return 0;
+ return ret;
 } /* pcmcia_register_socket */
 
 
@@ -407,8 +419,12 @@
                         remove_proc_entry(name, proc_pccard);
                 }
 #endif
-
- shutdown_socket(s);
+ if (s->thread) {
+ init_completion(&s->thread_done);
+ s->thread = NULL;
+ wake_up(&s->thread_wait);
+ wait_for_completion(&s->thread_done);
+ }
                 release_cis_mem(s);
                 while (s->clients) {
                         client = s->clients;
@@ -450,15 +466,6 @@
 
 static int send_event(socket_info_t *s, event_t event, int priority);
 
-/*
- * Sleep for n_cs centiseconds (1 cs = 1/100th of a second)
- */
-static void cs_sleep(unsigned int n_cs)
-{
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout( (n_cs * HZ + 99) / 100);
-}
-
 static void shutdown_socket(socket_info_t *s)
 {
     client_t **c;
@@ -505,132 +512,6 @@
     free_regions(&s->c_region);
 } /* shutdown_socket */
 
-/*
- * Return zero if we think the card isn't actually present
- */
-static int setup_socket(socket_info_t *s)
-{
- int val, ret;
- int setup_timeout = 100;
-
- /* Wait for "not pending" */
- for (;;) {
- get_socket_status(s, &val);
- if (!(val & SS_PENDING))
- break;
- if (--setup_timeout) {
- cs_sleep(10);
- continue;
- }
- printk(KERN_NOTICE "cs: socket %p voltage interrogation"
- " timed out\n", s);
- ret = 0;
- goto out;
- }
-
- if (val & SS_DETECT) {
- DEBUG(1, "cs: setup_socket(%p): applying power\n", s);
- s->state |= SOCKET_PRESENT;
- s->socket.flags &= SS_DEBOUNCED;
- if (val & SS_3VCARD)
- s->socket.Vcc = s->socket.Vpp = 33;
- else if (!(val & SS_XVCARD))
- s->socket.Vcc = s->socket.Vpp = 50;
- else {
- printk(KERN_NOTICE "cs: socket %p: unsupported "
- "voltage key\n", s);
- s->socket.Vcc = 0;
- }
- if (val & SS_CARDBUS) {
- s->state |= SOCKET_CARDBUS;
-#ifndef CONFIG_CARDBUS
- printk(KERN_NOTICE "cs: unsupported card type detected!\n");
-#endif
- }
- set_socket(s, &s->socket);
- cs_sleep(vcc_settle);
- reset_socket(s);
- ret = 1;
- } else {
- DEBUG(0, "cs: setup_socket(%p): no card!\n", s);
- ret = 0;
- }
-out:
- return ret;
-} /* setup_socket */
-
-/*======================================================================
-
- Reset_socket() and unreset_socket() handle hard resets. Resets
- have several causes: card insertion, a call to reset_socket, or
- recovery from a suspend/resume cycle. Unreset_socket() sends
- a CS event that matches the cause of the reset.
-
-======================================================================*/
-
-static void reset_socket(socket_info_t *s)
-{
- DEBUG(1, "cs: resetting socket %p\n", s);
- s->socket.flags |= SS_OUTPUT_ENA | SS_RESET;
- set_socket(s, &s->socket);
- udelay((long)reset_time);
- s->socket.flags &= ~SS_RESET;
- set_socket(s, &s->socket);
- cs_sleep(unreset_delay);
- unreset_socket(s);
-} /* reset_socket */
-
-#define EVENT_MASK \
-(SOCKET_SETUP_PENDING|SOCKET_SUSPEND|SOCKET_RESET_PENDING)
-
-static void unreset_socket(socket_info_t *s)
-{
- int setup_timeout = unreset_limit;
- int val;
-
- /* Wait for "ready" */
- for (;;) {
- get_socket_status(s, &val);
- if (val & SS_READY)
- break;
- DEBUG(2, "cs: socket %d not ready yet\n", s->sock);
- if (--setup_timeout) {
- cs_sleep(unreset_check);
- continue;
- }
- printk(KERN_NOTICE "cs: socket %p timed out during"
- " reset. Try increasing setup_delay.\n", s);
- s->state &= ~EVENT_MASK;
- return;
- }
-
- DEBUG(1, "cs: reset done on socket %p\n", s);
- if (s->state & SOCKET_SUSPEND) {
- s->state &= ~EVENT_MASK;
- if (verify_cis_cache(s) != 0)
- parse_events(s, SS_DETECT);
- else
- send_event(s, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
- } else if (s->state & SOCKET_SETUP_PENDING) {
-#ifdef CONFIG_CARDBUS
- if (s->state & SOCKET_CARDBUS) {
- cb_alloc(s);
- s->state |= SOCKET_CARDBUS_CONFIG;
- }
-#endif
- send_event(s, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
- s->state &= ~SOCKET_SETUP_PENDING;
- } else {
- send_event(s, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW);
- if (s->reset_handle) {
- s->reset_handle->event_callback_args.info = NULL;
- EVENT(s->reset_handle, CS_EVENT_RESET_COMPLETE,
- CS_EVENT_PRI_LOW);
- }
- s->state &= ~EVENT_MASK;
- }
-} /* unreset_socket */
-
 /*======================================================================
 
     The central event handler. Send_event() sends an event to all
@@ -661,61 +542,266 @@
     return ret;
 } /* send_event */
 
-static void do_shutdown(socket_info_t *s)
+static void pcmcia_error(socket_info_t *skt, const char *fmt, ...)
 {
- client_t *client;
- if (s->state & SOCKET_SHUTDOWN_PENDING)
- return;
- s->state |= SOCKET_SHUTDOWN_PENDING;
- send_event(s, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
- for (client = s->clients; client; client = client->next)
- if (!(client->Attributes & INFO_MASTER_CLIENT))
- client->state |= CLIENT_STALE;
- if (s->state & (SOCKET_SETUP_PENDING|SOCKET_RESET_PENDING)) {
- DEBUG(0, "cs: flushing pending setup\n");
- s->state &= ~EVENT_MASK;
- }
- cs_sleep(shutdown_delay);
- s->state &= ~SOCKET_PRESENT;
- shutdown_socket(s);
+ static char buf[128];
+ va_list ap;
+ int len;
+
+ va_start(ap, fmt);
+ len = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ buf[len] = '\0';
+
+ printk(KERN_ERR "PCMCIA: socket %p: %s", skt, buf);
+}
+
+#define cs_to_timeout(cs) (((cs) * HZ + 99) / 100)
+
+static void socket_remove_drivers(socket_info_t *skt)
+{
+ client_t *client;
+
+ send_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
+
+ for (client = skt->clients; client; client = client->next)
+ if (!(client->Attributes & INFO_MASTER_CLIENT))
+ client->state |= CLIENT_STALE;
+}
+
+static void socket_shutdown(socket_info_t *skt)
+{
+ socket_remove_drivers(skt);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cs_to_timeout(shutdown_delay));
+ skt->state &= ~SOCKET_PRESENT;
+ shutdown_socket(skt);
+}
+
+static int socket_reset(socket_info_t *skt)
+{
+ int status, i;
+
+ skt->socket.flags |= SS_OUTPUT_ENA | SS_RESET;
+ set_socket(skt, &skt->socket);
+ udelay((long)reset_time);
+
+ skt->socket.flags &= ~SS_RESET;
+ set_socket(skt, &skt->socket);
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cs_to_timeout(unreset_delay));
+ for (i = 0; i < unreset_limit; i++) {
+ get_socket_status(skt, &status);
+
+ if (!(status & SS_DETECT))
+ return CS_NO_CARD;
+
+ if (status & SS_READY)
+ return CS_SUCCESS;
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cs_to_timeout(unreset_check));
+ }
+
+ pcmcia_error(skt, "time out after reset.\n");
+ return CS_GENERAL_FAILURE;
+}
+
+static int socket_setup(socket_info_t *skt, int initial_delay)
+{
+ int status, i;
+
+ get_socket_status(skt, &status);
+ if (!(status & SS_DETECT))
+ return CS_NO_CARD;
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cs_to_timeout(initial_delay));
+
+ for (i = 0; i < 100; i++) {
+ get_socket_status(skt, &status);
+ if (!(status & SS_DETECT))
+ return CS_NO_CARD;
+
+ if (!(status & SS_PENDING))
+ break;
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cs_to_timeout(10));
+ }
+
+ if (status & SS_PENDING) {
+ pcmcia_error(skt, "voltage interrogation timed out.\n");
+ return CS_GENERAL_FAILURE;
+ }
+
+ if (status & SS_CARDBUS) {
+ skt->state |= SOCKET_CARDBUS;
+#ifndef CONFIG_CARDBUS
+ pcmcia_error(skt, "cardbus cards are not supported.\n");
+ return CS_BAD_TYPE;
+#endif
+ }
+
+ /*
+ * Decode the card voltage requirements, and apply power to the card.
+ */
+ if (status & SS_3VCARD)
+ skt->socket.Vcc = skt->socket.Vpp = 33;
+ else if (!(status & SS_XVCARD))
+ skt->socket.Vcc = skt->socket.Vpp = 50;
+ else {
+ pcmcia_error(skt, "unsupported voltage key.\n");
+ return CS_BAD_TYPE;
+ }
+ skt->state |= SOCKET_PRESENT;
+ skt->socket.flags = SS_DEBOUNCED;
+ set_socket(skt, &skt->socket);
+
+ /*
+ * Wait "vcc_settle" for the supply to stabilise.
+ */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(cs_to_timeout(vcc_settle));
+
+ return socket_reset(skt);
+}
+
+/*
+ * Handle card insertion. Setup the socket, reset the card,
+ * and then tell the rest of PCMCIA that a card is present.
+ */
+static int socket_insert(socket_info_t *skt)
+{
+ int ret;
+
+ ret = socket_setup(skt, setup_delay);
+ if (ret == CS_SUCCESS) {
+#ifdef CONFIG_CARDBUS
+ if (skt->state & SOCKET_CARDBUS) {
+ cb_alloc(skt);
+ skt->state |= SOCKET_CARDBUS_CONFIG;
+ }
+#endif
+ send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
+ skt->socket.flags &= ~SS_DEBOUNCED;
+ } else
+ socket_shutdown(skt);
+
+ return ret;
+}
+
+static int socket_suspend(socket_info_t *skt)
+{
+ if (skt->state & SOCKET_SUSPEND)
+ return CS_IN_USE;
+
+ send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
+ suspend_socket(skt);
+ skt->state |= SOCKET_SUSPEND;
+
+ return CS_SUCCESS;
+}
+
+/*
+ * Resume a socket. If a card is present, verify its CIS against
+ * our cached copy. If they are different, the card has been
+ * replaced, and we need to tell the drivers.
+ */
+static int socket_resume(socket_info_t *skt)
+{
+ int ret;
+
+ if (!(skt->state & SOCKET_SUSPEND))
+ return CS_IN_USE;
+
+ init_socket(skt);
+
+ ret = socket_setup(skt, resume_delay);
+ if (ret == CS_SUCCESS) {
+ /*
+ * FIXME: need a better check here for cardbus cards.
+ */
+ if (verify_cis_cache(skt) != 0) {
+ socket_remove_drivers(skt);
+ destroy_cis_cache(skt);
+ send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
+ } else {
+ send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
+ }
+ skt->socket.flags &= ~SS_DEBOUNCED;
+ } else
+ socket_shutdown(skt);
+
+ skt->state &= ~SOCKET_SUSPEND;
+
+ return CS_SUCCESS;
+}
+
+static int pccardd(void *__skt)
+{
+ socket_info_t *skt = __skt;
+ DECLARE_WAITQUEUE(wait, current);
+
+ daemonize("pccardd");
+ skt->thread = current;
+ complete(&skt->thread_done);
+
+ add_wait_queue(&skt->thread_wait, &wait);
+ for (;;) {
+ unsigned long flags;
+ unsigned int events;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_irqsave(&skt->thread_lock, flags);
+ events = skt->thread_events;
+ skt->thread_events = 0;
+ spin_unlock_irqrestore(&skt->thread_lock, flags);
+
+ if (events) {
+ down(&skt->skt_sem);
+ if (events & SS_DETECT && !(skt->state & SOCKET_SUSPEND)) {
+ int status;
+
+ get_socket_status(skt, &status);
+ if ((skt->state & SOCKET_PRESENT) &&
+ !(status & SS_DETECT))
+ socket_shutdown(skt);
+ if (status & SS_DETECT)
+ socket_insert(skt);
+ }
+ if (events & SS_BATDEAD)
+ send_event(skt, CS_EVENT_BATTERY_DEAD, CS_EVENT_PRI_LOW);
+ if (events & SS_BATWARN)
+ send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW);
+ if (events & SS_READY)
+ send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW);
+ up(&skt->skt_sem);
+ continue;
+ }
+
+ schedule();
+ if (!skt->thread)
+ break;
+ }
+ remove_wait_queue(&skt->thread_wait, &wait);
+
+ socket_shutdown(skt);
+
+ complete_and_exit(&skt->thread_done, 0);
 }
 
 static void parse_events(void *info, u_int events)
 {
- socket_info_t *s = info;
- if (events & SS_DETECT) {
- int status;
-
- get_socket_status(s, &status);
- if ((s->state & SOCKET_PRESENT) &&
- (!(s->state & SOCKET_SUSPEND) ||
- !(status & SS_DETECT)))
- do_shutdown(s);
- if (status & SS_DETECT) {
- if (s->state & SOCKET_SETUP_PENDING) {
- DEBUG(1, "cs: delaying pending setup\n");
- return;
- }
- s->state |= SOCKET_SETUP_PENDING;
- if (s->state & SOCKET_SUSPEND)
- cs_sleep(resume_delay);
- else
- cs_sleep(setup_delay);
- s->socket.flags |= SS_DEBOUNCED;
- if (setup_socket(s) == 0)
- s->state &= ~SOCKET_SETUP_PENDING;
- s->socket.flags &= ~SS_DEBOUNCED;
- }
- }
- if (events & SS_BATDEAD)
- send_event(s, CS_EVENT_BATTERY_DEAD, CS_EVENT_PRI_LOW);
- if (events & SS_BATWARN)
- send_event(s, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW);
- if (events & SS_READY) {
- if (!(s->state & SOCKET_RESET_PENDING))
- send_event(s, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW);
- else DEBUG(1, "cs: ready change during reset\n");
- }
+ socket_info_t *s = info;
+
+ spin_lock(&s->thread_lock);
+ s->thread_events |= events;
+ spin_unlock(&s->thread_lock);
+
+ wake_up(&s->thread_wait);
 } /* parse_events */
 
 /*======================================================================
@@ -727,27 +813,18 @@
     
 ======================================================================*/
 
-void pcmcia_suspend_socket (socket_info_t *s)
+void pcmcia_suspend_socket (socket_info_t *skt)
 {
- if ((s->state & SOCKET_PRESENT) && !(s->state & SOCKET_SUSPEND)) {
- send_event(s, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
- suspend_socket(s);
- s->state |= SOCKET_SUSPEND;
- }
+ down(&skt->skt_sem);
+ socket_suspend(skt);
+ up(&skt->skt_sem);
 }
 
-void pcmcia_resume_socket (socket_info_t *s)
+void pcmcia_resume_socket (socket_info_t *skt)
 {
- int stat;
-
- /* Do this just to reinitialize the socket */
- init_socket(s);
- get_socket_status(s, &stat);
-
- /* If there was or is a card here, we need to do something
- about it... but parse_events will sort it all out. */
- if ((s->state & SOCKET_PRESENT) || (stat & SS_DETECT))
- parse_events(s, SS_DETECT);
+ down(&skt->skt_sem);
+ socket_resume(skt);
+ up(&skt->skt_sem);
 }
 
 
@@ -1461,15 +1538,8 @@
 
     s = socket_table[ns];
     if (++s->real_clients == 1) {
- int status;
         register_callback(s, &parse_events, s);
- get_socket_status(s, &status);
- if ((status & SS_DETECT) &&
- !(s->state & SOCKET_SETUP_PENDING)) {
- s->state |= SOCKET_SETUP_PENDING;
- if (setup_socket(s) == 0)
- s->state &= ~SOCKET_SETUP_PENDING;
- }
+ parse_events(s, SS_DETECT);
     }
 
     *handle = client;
@@ -2022,30 +2092,44 @@
 
 int pcmcia_reset_card(client_handle_t handle, client_req_t *req)
 {
- int i, ret;
- socket_info_t *s;
+ socket_info_t *skt;
+ int ret;
     
- if (CHECK_HANDLE(handle))
- return CS_BAD_HANDLE;
- i = handle->Socket; s = socket_table[i];
- if (!(s->state & SOCKET_PRESENT))
- return CS_NO_CARD;
- if (s->state & SOCKET_RESET_PENDING)
- return CS_IN_USE;
- s->state |= SOCKET_RESET_PENDING;
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ DEBUG(1, "cs: resetting socket %d\n", handle->Socket);
+ skt = SOCKET(handle);
+
+ down(&skt->skt_sem);
+ do {
+ if (!(skt->state & SOCKET_PRESENT)) {
+ ret = CS_NO_CARD;
+ break;
+ }
+ if (skt->state & SOCKET_SUSPEND) {
+ ret = CS_IN_USE;
+ break;
+ }
+ if (skt->state & SOCKET_CARDBUS) {
+ ret = CS_UNSUPPORTED_FUNCTION;
+ break;
+ }
 
- ret = send_event(s, CS_EVENT_RESET_REQUEST, CS_EVENT_PRI_LOW);
- if (ret != 0) {
- s->state &= ~SOCKET_RESET_PENDING;
- handle->event_callback_args.info = (void *)(u_long)ret;
- EVENT(handle, CS_EVENT_RESET_COMPLETE, CS_EVENT_PRI_LOW);
- } else {
- DEBUG(1, "cs: resetting socket %d\n", i);
- send_event(s, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW);
- s->reset_handle = handle;
- reset_socket(s);
- }
- return CS_SUCCESS;
+ ret = send_event(skt, CS_EVENT_RESET_REQUEST, CS_EVENT_PRI_LOW);
+ if (ret == 0) {
+ send_event(skt, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW);
+ if (socket_reset(skt) == CS_SUCCESS)
+ send_event(skt, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW);
+ }
+
+ handle->event_callback_args.info = (void *)(u_long)ret;
+ EVENT(handle, CS_EVENT_RESET_COMPLETE, CS_EVENT_PRI_LOW);
+
+ ret = CS_SUCCESS;
+ } while (0);
+ up(&skt->skt_sem);
+
+ return ret;
 } /* reset_card */
 
 /*======================================================================
@@ -2057,42 +2141,56 @@
 
 int pcmcia_suspend_card(client_handle_t handle, client_req_t *req)
 {
- int i;
- socket_info_t *s;
+ socket_info_t *skt;
+ int ret;
     
- if (CHECK_HANDLE(handle))
- return CS_BAD_HANDLE;
- i = handle->Socket; s = socket_table[i];
- if (!(s->state & SOCKET_PRESENT))
- return CS_NO_CARD;
- if (s->state & SOCKET_SUSPEND)
- return CS_IN_USE;
-
- DEBUG(1, "cs: suspending socket %d\n", i);
- send_event(s, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
- suspend_socket(s);
- s->state |= SOCKET_SUSPEND;
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ DEBUG(1, "cs: suspending socket %d\n", handle->Socket);
+ skt = SOCKET(handle);
+
+ down(&skt->skt_sem);
+ do {
+ if (!(skt->state & SOCKET_PRESENT)) {
+ ret = CS_NO_CARD;
+ break;
+ }
+ if (skt->state & SOCKET_CARDBUS) {
+ ret = CS_UNSUPPORTED_FUNCTION;
+ break;
+ }
+ ret = socket_suspend(skt);
+ } while (0);
+ up(&skt->skt_sem);
 
- return CS_SUCCESS;
+ return ret;
 } /* suspend_card */
 
 int pcmcia_resume_card(client_handle_t handle, client_req_t *req)
 {
- int i;
- socket_info_t *s;
+ socket_info_t *skt;
+ int ret;
     
- if (CHECK_HANDLE(handle))
- return CS_BAD_HANDLE;
- i = handle->Socket; s = socket_table[i];
- if (!(s->state & SOCKET_PRESENT))
- return CS_NO_CARD;
- if (!(s->state & SOCKET_SUSPEND))
- return CS_IN_USE;
-
- DEBUG(1, "cs: waking up socket %d\n", i);
- setup_socket(s);
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ DEBUG(1, "cs: waking up socket %d\n", handle->Socket);
+ skt = SOCKET(handle);
+
+ down(&skt->skt_sem);
+ do {
+ if (!(skt->state & SOCKET_PRESENT)) {
+ ret = CS_NO_CARD;
+ break;
+ }
+ if (skt->state & SOCKET_CARDBUS) {
+ ret = CS_UNSUPPORTED_FUNCTION;
+ break;
+ }
+ ret = socket_resume(skt);
+ } while (0);
+ up(&skt->skt_sem);
 
- return CS_SUCCESS;
+ return ret;
 } /* resume_card */
 
 /*======================================================================
@@ -2103,57 +2201,58 @@
 
 int pcmcia_eject_card(client_handle_t handle, client_req_t *req)
 {
- int i, ret;
- socket_info_t *s;
- u_long flags;
+ socket_info_t *skt;
+ int ret;
     
- if (CHECK_HANDLE(handle))
- return CS_BAD_HANDLE;
- i = handle->Socket; s = socket_table[i];
- if (!(s->state & SOCKET_PRESENT))
- return CS_NO_CARD;
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ DEBUG(1, "cs: user eject request on socket %d\n", handle->Socket);
+ skt = SOCKET(handle);
+
+ down(&skt->skt_sem);
+ do {
+ if (!(skt->state & SOCKET_PRESENT)) {
+ ret = CS_NO_CARD;
+ break;
+ }
 
- DEBUG(1, "cs: user eject request on socket %d\n", i);
+ ret = send_event(skt, CS_EVENT_EJECTION_REQUEST, CS_EVENT_PRI_LOW);
+ if (ret != 0)
+ break;
 
- ret = send_event(s, CS_EVENT_EJECTION_REQUEST, CS_EVENT_PRI_LOW);
- if (ret != 0)
- return ret;
+ socket_shutdown(skt);
+ ret = CS_SUCCESS;
+ } while (0);
+ up(&skt->skt_sem);
 
- spin_lock_irqsave(&s->lock, flags);
- do_shutdown(s);
- spin_unlock_irqrestore(&s->lock, flags);
-
- return CS_SUCCESS;
-
+ return ret;
 } /* eject_card */
 
 int pcmcia_insert_card(client_handle_t handle, client_req_t *req)
 {
- int i, status;
- socket_info_t *s;
- u_long flags;
-
- if (CHECK_HANDLE(handle))
- return CS_BAD_HANDLE;
- i = handle->Socket; s = socket_table[i];
- if (s->state & SOCKET_PRESENT)
- return CS_IN_USE;
-
- DEBUG(1, "cs: user insert request on socket %d\n", i);
+ socket_info_t *skt;
+ int ret;
 
- spin_lock_irqsave(&s->lock, flags);
- if (!(s->state & SOCKET_SETUP_PENDING)) {
- s->state |= SOCKET_SETUP_PENDING;
- spin_unlock_irqrestore(&s->lock, flags);
- get_socket_status(s, &status);
- if ((status & SS_DETECT) == 0 || (setup_socket(s) == 0)) {
- s->state &= ~SOCKET_SETUP_PENDING;
- return CS_NO_CARD;
- }
- } else
- spin_unlock_irqrestore(&s->lock, flags);
+ if (CHECK_HANDLE(handle))
+ return CS_BAD_HANDLE;
+ DEBUG(1, "cs: user insert request on socket %d\n", handle->Socket);
+ skt = SOCKET(handle);
+
+ down(&skt->skt_sem);
+ do {
+ if (skt->state & SOCKET_PRESENT) {
+ ret = CS_IN_USE;
+ break;
+ }
+ if (socket_insert(skt) == CS_NO_CARD) {
+ ret = CS_NO_CARD;
+ break;
+ }
+ ret = CS_SUCCESS;
+ } while (0);
+ up(&skt->skt_sem);
 
- return CS_SUCCESS;
+ return ret;
 } /* insert_card */
 
 /*======================================================================
diff -u /net/flint/usrc/linux-bk-2.5/linux-2.5-pcmcia/drivers/pcmcia/cs_internal.h linux/drivers/pcmcia/cs_internal.h
--- /net/flint/usrc/linux-bk-2.5/linux-2.5-pcmcia/drivers/pcmcia/cs_internal.h Sat Apr 26 23:01:03 2003
+++ linux/drivers/pcmcia/cs_internal.h Sat Apr 26 21:34:59 2003
@@ -133,7 +133,6 @@
     u_short lock_count;
     client_handle_t clients;
     u_int real_clients;
- client_handle_t reset_handle;
     pccard_mem_map cis_mem;
     u_char *cis_virt;
     config_t *config;
@@ -155,6 +154,14 @@
 #ifdef CONFIG_PROC_FS
     struct proc_dir_entry *proc;
 #endif
+
+ struct semaphore skt_sem; /* protects socket h/w state */
+
+ struct task_struct *thread;
+ struct completion thread_done;
+ wait_queue_head_t thread_wait;
+ spinlock_t thread_lock; /* protects thread_events */
+ unsigned int thread_events;
 } socket_info_t;
 
 /* Flags in config state */

-- 
Russell King (rmk@arm.linux.org.uk)                The developer of ARM Linux
             http://www.arm.linux.org.uk/personal/aboutme.html

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Thu May 15 2003 - 22:00:42 EST