 25-akpm/drivers/net/wireless/orinoco_cs.c |    1 
 25-akpm/drivers/pcmcia/cs.c               |  827 +++++++++++++++++++++---------
 25-akpm/drivers/pcmcia/cs_internal.h      |   18 
 25-akpm/drivers/pcmcia/rsrc_mgr.c         |   20 
 25-akpm/include/pcmcia/bus_ops.h          |    2 
 25-akpm/include/pcmcia/driver_ops.h       |    2 
 25-akpm/include/pcmcia/ds.h               |    2 
 7 files changed, 617 insertions(+), 255 deletions(-)

diff -puN drivers/net/wireless/orinoco_cs.c~pcmcia-20030421 drivers/net/wireless/orinoco_cs.c
--- 25/drivers/net/wireless/orinoco_cs.c~pcmcia-20030421	Mon Apr 21 17:38:29 2003
+++ 25-akpm/drivers/net/wireless/orinoco_cs.c	Mon Apr 21 17:38:29 2003
@@ -35,7 +35,6 @@
 #include <pcmcia/cistpl.h>
 #include <pcmcia/cisreg.h>
 #include <pcmcia/ds.h>
-#include <pcmcia/bus_ops.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
diff -puN drivers/pcmcia/cs.c~pcmcia-20030421 drivers/pcmcia/cs.c
--- 25/drivers/pcmcia/cs.c~pcmcia-20030421	Mon Apr 21 17:38:29 2003
+++ 25-akpm/drivers/pcmcia/cs.c	Mon Apr 21 17:38:29 2003
@@ -82,6 +82,8 @@
 #define OPTIONS PCI_OPT CB_OPT PM_OPT
 #endif
 
+#undef EVENT_DEBUG
+
 static const char *release = "Linux Kernel Card Services " CS_RELEASE;
 static const char *options = "options: " OPTIONS;
 
@@ -127,6 +129,19 @@ socket_state_t dead_socket = {
 socket_t sockets = 0;
 socket_info_t *socket_table[MAX_SOCK];
 
+/*
+ * Our private work queue.  We need this because we will be running
+ * driver probe and remove functions from our workqueue.  If we use
+ * keventd, and their remove function does a flush_scheduled_work(),
+ * we will deadlock.
+ *
+ * The unfortunate side effect of this is that we will have one
+ * pcmcia thread per CPU; unfortunately there doesn't seem to be a
+ * way of creating just one thread with the current workqueue
+ * implementation.
+ */
+static struct workqueue_struct *pcmcia_wq;
+
 #ifdef CONFIG_PROC_FS
 struct proc_dir_entry *proc_pccard = NULL;
 #endif
@@ -302,11 +317,10 @@ static int proc_read_clients(char *buf, 
     
 ======================================================================*/
 
-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 void sm_init(socket_info_t *s);
+static void sm_exit(socket_info_t *s);
 
 #define to_class_data(dev) dev->class_data
 
@@ -343,7 +357,8 @@ int pcmcia_register_socket(struct device
 		s->cis_mem.speed = cis_speed;
 		s->erase_busy.next = s->erase_busy.prev = &s->erase_busy;
 		spin_lock_init(&s->lock);
-    
+		sm_init(s);
+
 		/* TBD: remove usage of socket_table, use class_for_each_dev instead */
 		for (j = 0; j < sockets; j++)
 			if (socket_table[j] == NULL) break;
@@ -406,8 +421,8 @@ void pcmcia_unregister_socket(struct dev
 			remove_proc_entry(name, proc_pccard);
 		}
 #endif
-		
-		shutdown_socket(s);
+
+		sm_exit(s);
 		release_cis_mem(s);
 		while (s->clients) {
 			client = s->clients;
@@ -449,15 +464,6 @@ static void free_regions(memory_handle_t
 
 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 +511,9 @@ static void shutdown_socket(socket_info_
     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,62 +544,556 @@ static int send_event(socket_info_t *s, 
     return ret;
 } /* send_event */
 
-static void do_shutdown(socket_info_t *s)
+/*
+ * Event mask.
+ */
+#define EV_TIMER	(1<<0)
+#define EV_DETECT	(1<<1)
+#define EV_READY	(1<<2)
+#define EV_SUSPEND	(1<<3)
+#define EV_RESUME	(1<<4)
+#define EV_RESOURCES	(1<<5)
+#define EV_RESET	(1<<6)
+#define EV_EJECT	(1<<7)
+#define EV_INSERT	(1<<8)
+#define EV_FORCE	(1<<31)
+
+static void state_error(socket_info_t *s, unsigned int status, unsigned int events);
+static void state_empty(socket_info_t *s, unsigned int status, unsigned int events);
+static void state_setup(socket_info_t *s, unsigned int status, unsigned int events);
+static void state_reset(socket_info_t *s, unsigned int status, unsigned int events);
+static void state_wait_ready(socket_info_t *s, unsigned int status, unsigned int events);
+static void state_cardpresent(socket_info_t *s, unsigned int status, unsigned int events);
+static void state_suspend(socket_info_t *s, unsigned int status, unsigned int events);
+static void state_removed(socket_info_t *s, unsigned int status, unsigned int events);
+static void state_shutdown(socket_info_t *s, unsigned int status, unsigned int events);
+
+static inline void __sm_event_queue(socket_info_t *s, unsigned int event)
 {
-    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);
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->sm.lock, flags);
+	s->sm.event_pending |= event;
+	spin_unlock_irqrestore(&s->sm.lock, flags);
 }
 
-static void parse_events(void *info, u_int events)
+static inline void sm_event_clear(socket_info_t *s, unsigned int event)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&s->sm.lock, flags);
+	s->sm.event_pending &= ~event;
+	spin_unlock_irqrestore(&s->sm.lock, flags);
+}
+
+static void sm_event_ack(socket_info_t *s, unsigned int events)
+{
+	unsigned long flags;
+
+	if (events & EV_RESET)
+		s->state &= ~SOCKET_RESET_PENDING;
+	if (events & EV_DETECT)
+		s->state &= ~(SOCKET_SETUP_PENDING|SOCKET_RESET_PENDING);
+
+	spin_lock_irqsave(&s->sm.lock, flags);
+	s->sm.event_ack |= events;
+	s->sm.event_pending &= ~events;
+	spin_unlock_irqrestore(&s->sm.lock, flags);
+
+	if (events)
+		wake_up(&s->sm.wait_ack);
+}
+
+static inline void sm_set_timer(socket_info_t *s, unsigned int cs)
 {
-    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");
+	sm_event_clear(s, EV_TIMER);
+	s->sm.event_mask |= EV_TIMER;
+	mod_timer(&s->sm.timer, jiffies + ((cs * HZ + 99) / 100));
+}
+
+/*
+ * Move to the next state.
+ */
+static void
+sm_next(socket_info_t *s,
+	void (*fn)(socket_info_t *,unsigned int,unsigned int),
+	unsigned int mask)
+{
+	s->sm.event_mask = mask | EV_FORCE;
+	s->sm.func = fn;
+	__sm_event_queue(s, EV_FORCE);
+}
+
+
+static void sm_error(socket_info_t *s, const char *fmt, ...)
+{
+	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", s, buf);
+
+	sm_event_clear(s, EV_DETECT|EV_READY|EV_RESET);
+	sm_next(s, state_error, 0);
+}
+
+/*
+ * We hit an error.  Stop the state machine, but respond to
+ * suspend, resume, reset and resources events.
+ */
+static void state_error(socket_info_t *s, unsigned int status, unsigned int events)
+{
+	/*
+	 * We hit an error.  Ensure that the socket is off.
+	 */
+	s->socket.flags &= ~(SS_DEBOUNCED|SS_OUTPUT_ENA|SS_RESET|SS_IOCARD);
+	s->socket.Vcc = 0;
+	s->socket.Vpp = 0;
+	set_socket(s, &s->socket);
+	s->state &= ~SOCKET_SUSPEND;
+
+	s->sm.event_mask = EV_RESET|EV_SUSPEND|EV_RESUME|EV_INSERT|EV_EJECT|EV_DETECT|EV_RESOURCES;
+
+	/*
+	 * If we received a reset event from the user, re-detect
+	 * the card.  This will automatically reset the card.
+	 */
+	if (events & (EV_RESET|EV_RESOURCES|EV_EJECT)) {
+		s->sm.event_mask = EV_DETECT|EV_INSERT|EV_SUSPEND;
+		s->sm.func = state_empty;
+		__sm_event_queue(s, EV_DETECT);
+	}
+
+	sm_event_ack(s, events & (EV_SUSPEND|EV_RESUME|EV_INSERT|EV_DETECT));
+}
+
+/*
+ * The socket is empty.  If a card is inserted (or we receive an
+ * insert request,) setup and reset the socket.  If we receive a
+ * suspend, go to the suspend state and acknowledge the suspend.
+ */
+static void
+state_empty(socket_info_t *s, unsigned int status, unsigned int events)
+{
+	if (events & (EV_DETECT|EV_INSERT) && status & SS_DETECT) {
+		s->socket.flags = SS_DEBOUNCED;
+		s->sm.tries = 100;
+		s->sm.func = state_setup;
+		s->sm.event_mask = 0;
+
+		s->state |= SOCKET_SETUP_PENDING;
+
+		sm_set_timer(s, setup_delay);
 		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");
-    }
-} /* parse_events */
+	}
+
+	if (events & EV_SUSPEND) {
+		s->sm.func = state_suspend;
+		s->sm.event_mask = EV_RESUME;
+	}
+
+	/* Acknowledge all other events. */
+	sm_event_ack(s, events);
+}
+
+/*
+ * The setup/resume delay has expired.  Wait for the socket hardware to
+ * debounce the voltage sense/card type sense, claim a resource for the
+ * CIS, and apply power to the card.  Generally, we receive timer events,
+ * unless we are unable to grab resources for CIS.
+ */
+static void
+state_setup(socket_info_t *s, unsigned int status, unsigned int events)
+{
+	int ret;
+
+	if (events & EV_RESET)
+		sm_event_ack(s, EV_RESET);
+
+	if (!(status & SS_DETECT) || events & EV_EJECT) {
+		sm_next(s, state_removed, EV_DETECT|EV_INSERT);
+		return;
+	}
+
+	if (events & EV_SUSPEND) {
+		sm_next(s, state_suspend, EV_SUSPEND);
+		return;
+	}
+
+	/*
+	 * Some sockets debounce the voltage sense themselves. Wait for
+	 * the debounce period to end, indicated by SS_PENDING clear.
+	 */
+	if (status & SS_PENDING) {
+		if (events & EV_TIMER) {
+			if (--s->sm.tries) {
+				sm_set_timer(s, 10);
+			} else {
+				sm_error(s, "voltage interrogation timed out\n");
+			}
+		}
+		return;
+	}
+
+	if (status & SS_CARDBUS) {
+		s->state |= SOCKET_CARDBUS;
+#ifndef CONFIG_CARDBUS
+		sm_error(s, "cardbus cards are not supported\n");
+		return;
+#endif
+	}
+
+	s->state |= SOCKET_PRESENT;
+
+	/*
+	 * Decode the card voltage requirements, and apply power to the card.
+	 */
+	if (status & SS_3VCARD)
+		s->socket.Vcc = s->socket.Vpp = 33;
+	else if (!(status & SS_XVCARD))
+		s->socket.Vcc = s->socket.Vpp = 50;
+	else {
+		sm_error(s, "unsupported voltage key\n");
+		return;
+	}
+
+	set_socket(s, &s->socket);
+
+	/*
+	 * Wait "vcc_settle" for the supply to stabilise, and reset
+	 * the card.  Note that we only want to see timer events.
+	 */
+	s->sm.event_mask = 0;
+	s->sm.func = state_reset;
+	sm_set_timer(s, vcc_settle);
+}
+
+/*
+ * Enable the socket tri-state drivers, and reset the card.
+ * Note that we also clear any pending reset events.
+ */
+static void state_reset(socket_info_t *s, unsigned int status, unsigned int events)
+{
+	if (!(status & SS_DETECT)) {
+		sm_next(s, state_removed, 0);
+		return;
+	}
+
+	sm_event_clear(s, EV_READY);
+
+	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);
+
+	s->sm.tries = unreset_limit;
+	s->sm.func = state_wait_ready;
+	sm_set_timer(s, unreset_delay);
+}
+
+/*
+ * Wait for the card to signal that it is ready for us to read things
+ * like the CIS.  We also allow the user to request the card to be
+ * reset.  We only deal with eject, reset and ready events here.
+ */
+static void state_wait_ready(socket_info_t *s, unsigned int status, unsigned int events)
+{
+	if (!(status & SS_DETECT) || events & EV_EJECT) {
+		sm_next(s, state_removed, 0);
+		return;
+	}
+
+	if (!(status & SS_READY) && events & EV_TIMER) {
+		if (--s->sm.tries) {
+			s->sm.event_mask = EV_EJECT|EV_READY;
+			sm_set_timer(s, unreset_check);
+		} else {
+			s->state &= ~EVENT_MASK;
+			sm_error(s, "timed out during reset.  Try increasing setup_delay.\n");
+		}
+		return;
+	}
+
+	s->sm.event_mask = EV_EJECT|EV_SUSPEND|EV_RESET|EV_READY|EV_DETECT;
+	s->sm.func = state_cardpresent;
+
+	if (s->state & SOCKET_SUSPEND) {
+		s->state &= ~(SOCKET_SUSPEND|SOCKET_SETUP_PENDING);
+		if (verify_cis_cache(s) != 0) {
+			__sm_event_queue(s, EV_DETECT);
+			sm_next(s, state_removed, 0);
+		} else
+			send_event(s, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
+		events = EV_RESUME|EV_INSERT;
+	} 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);
+		events = EV_DETECT|EV_INSERT;
+	} else if (s->state & SOCKET_RESET_PENDING) {
+		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);
+		}
+		events = EV_RESET;
+	}
+
+	sm_event_ack(s, events|EV_READY);
+}
+
+/*
+ * The card is present, and has indicated that it is ready.  We're
+ * ready to announce the device to the world.
+ */
+static void state_cardpresent(socket_info_t *s, unsigned int status, unsigned int events)
+{
+	if (!(status & SS_DETECT) || events & EV_EJECT) {
+		sm_next(s, state_removed, 0);
+		return;
+	}
+
+	if (events & EV_SUSPEND) {
+		sm_next(s, state_suspend, EV_SUSPEND);
+		return;
+	}
+
+	if (events & EV_RESET) {
+		sm_next(s, state_reset, EV_RESET);
+		return;
+	}
+
+	if (events & EV_READY)
+		send_event(s, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW);
+
+	sm_event_ack(s, events);
+}
+
+/*
+ * The socket was suspended.  Handle resume and eject events.
+ * Clear any pending detect events on resume.
+ *
+ * Handles: EV_SUSPEND|EV_EJECT|EV_RESUME
+ */
+static void state_suspend(socket_info_t *s, unsigned int status, unsigned int events)
+{
+	if (events & EV_SUSPEND) {
+		send_event(s, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
+		suspend_socket(s);
+		s->state |= SOCKET_SUSPEND;
+		s->sm.event_mask = EV_RESUME|EV_EJECT;
+		sm_event_ack(s, EV_DETECT|EV_SUSPEND);
+	}
+
+	if (events & EV_EJECT) {
+		sm_next(s, state_removed, 0);
+	} else if (events & EV_RESUME) {
+		/* Do this just to reinitialize the socket */
+		init_socket(s);
+		if (status & SS_DETECT) {
+			s->socket.flags = SS_DEBOUNCED;
+			s->sm.tries = 100;
+			s->sm.func = state_setup;
+			s->sm.event_mask = 0;
+			sm_set_timer(s, resume_delay);
+			sm_event_ack(s, EV_DETECT);
+		} else if (s->state & SOCKET_PRESENT) {
+			s->sm.func = state_removed;
+		} else {
+			s->sm.func = state_empty;
+			s->sm.event_mask = EV_DETECT|EV_RESET|EV_SUSPEND;
+			sm_event_ack(s, EV_RESUME|EV_DETECT);
+		}
+	}
+}
+
+/*
+ * We come here when the socket becomes empty.
+ * Which events are acknowledged depends on the event mask.
+ */
+static void state_removed(socket_info_t *s, unsigned int status, unsigned int events)
+{
+	client_t *client;
+
+	sm_event_ack(s, events);
+
+	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;
+	}
+
+	s->sm.func = state_shutdown;
+	s->sm.event_mask = 0;
+	sm_set_timer(s, shutdown_delay);
+}
+
+/*
+ * Complete socket shutdown.
+ *
+ * Handles: EV_TIMER
+ */
+static void state_shutdown(socket_info_t *s, unsigned int status, unsigned int events)
+{
+	s->state &= ~SOCKET_PRESENT;
+	shutdown_socket(s);
+
+	s->sm.func = state_empty;
+	s->sm.event_mask = EV_DETECT|EV_RESET|EV_SUSPEND|EV_INSERT;
+
+	sm_event_ack(s, EV_EJECT);
+}
+
+static void sm_do_work(void *data)
+{
+	socket_info_t *s = data;
+	unsigned int events;
+
+	do {
+		int status;
+
+		/*
+		 * Get pending events, and then read the status.
+		 * Always clear the timer.
+		 */
+		spin_lock_irq(&s->sm.lock);
+		events = s->sm.event_pending & s->sm.event_mask;
+		spin_unlock_irq(&s->sm.lock);
+
+		if (events == 0)
+			break;
+
+		get_socket_status(s, &status);
+
+#ifdef EVENT_DEBUG
+		printk("cs: sock %p old smfunc %p mask 0x%08x pending 0x%08x "
+			"sockstate 0x%08x status 0x%08x delivering 0x%08x\n",
+			s, s->sm.func, s->sm.event_mask, s->sm.event_pending,
+			s->state, status, events);
+#endif
+
+		if (events)
+			s->sm.func(s, status, events);
+
+#ifdef EVENT_DEBUG
+		printk("cs: sock %p new smfunc %p mask 0x%08x pending 0x%08x "
+			"sockstate 0x%08x\n",
+			s, s->sm.func, s->sm.event_mask, s->sm.event_pending,
+			s->state);
+#endif
+	} while (events);
+}
+
+static void sm_event_queue(socket_info_t *s, unsigned int event)
+{
+	__sm_event_queue(s, event);
+	if (event & s->sm.event_mask)
+		queue_work(pcmcia_wq, &s->sm.work);
+}
+
+static void sm_timer_expire(unsigned long data)
+{
+	socket_info_t *s = (socket_info_t *)data;
+	sm_event_queue(s, EV_TIMER);
+}
+
+static void sm_init(socket_info_t *s)
+{
+	s->sm.event_mask = EV_DETECT|EV_RESET|EV_SUSPEND;
+	s->sm.func = state_empty;
+
+	spin_lock_init(&s->sm.lock);
+
+	init_timer(&s->sm.timer);
+	s->sm.timer.data = (unsigned long)s;
+	s->sm.timer.function = sm_timer_expire;
+
+	init_waitqueue_head(&s->sm.wait_ack);
+
+	INIT_WORK(&s->sm.work, sm_do_work, s);
+}
+
+static void sm_exit(socket_info_t *s)
+{
+	del_timer_sync(&s->sm.timer);
+	flush_scheduled_work();
+	shutdown_socket(s);
+}
+
+/**
+ * sm_event_wait - queue an event and wait for acknowledgement
+ * @s: socket info structure describing socket
+ * @event: event to queue.
+ *
+ * Queue an event for the CS state machine to process, and wait
+ * for the state machine to acknowledge.
+ *
+ * Restrictions: If this function is called from the kernel
+ * event thread, we will deadlock.
+ */
+static void sm_event_wait(socket_info_t *s, unsigned int event)
+{
+#if 0
+	int is_keventd = current_is_keventd();
+	WARN_ON(is_keventd);
+	if (is_keventd)
+		return;
+#endif
+
+	s->sm.event_ack &= ~event;
+	sm_event_queue(s, event);
+	wait_event(s->sm.wait_ack, s->sm.event_ack & event);
+}
+
+void pcmcia_resources_changed(void)
+{
+	int i;
+
+	for (i = 0; i < sockets; i++) {
+		socket_info_t *s = socket_table[i];
+		if (!s)
+			continue;
+		sm_event_queue(s, EV_RESOURCES);
+#ifdef CONFIG_CARDBUS
+		cb_release_cis_mem(s);
+#endif
+	}
+}
+
+static void parse_events(void *info, u_int events)
+{
+	socket_info_t *s = info;
+	unsigned int mask = 0;
+
+	if (events & SS_DETECT)
+		mask |= EV_DETECT;
+	if (events & SS_READY)
+		mask |= EV_READY;
+
+	if (mask)
+		sm_event_queue(s, mask);
+
+	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);
+}
 
 /*======================================================================
 
@@ -729,25 +1106,12 @@ static void parse_events(void *info, u_i
 
 void pcmcia_suspend_socket (socket_info_t *s)
 {
-    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;
-    }
+	sm_event_wait(s, EV_SUSPEND);
 }
 
 void pcmcia_resume_socket (socket_info_t *s)
 {
-    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);
+	sm_event_wait(s, EV_RESUME);
 }
 
 
@@ -1446,6 +1810,7 @@ int pcmcia_register_client(client_handle
     client_t *client;
     socket_info_t *s;
     socket_t ns;
+    int nr_clients;
     
     /* Look for unbound client with matching dev_info */
     client = NULL;
@@ -1462,17 +1827,7 @@ int pcmcia_register_client(client_handle
 	return CS_OUT_OF_RESOURCE;
 
     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;
-	}
-    }
+    nr_clients = ++s->real_clients;
 
     *handle = client;
     client->state &= ~CLIENT_UNBOUND;
@@ -1512,6 +1867,10 @@ int pcmcia_register_client(client_handle
 	else
 	    client->PendingEvents |= CS_EVENT_CARD_INSERTION;
     }
+    if (nr_clients == 1) {
+	register_callback(s, &parse_events, s);
+	sm_event_queue(s, EV_DETECT);
+    }
     return CS_SUCCESS;
 } /* register_client */
 
@@ -2044,8 +2403,10 @@ int pcmcia_reset_card(client_handle_t ha
     } 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);
+
+	sm_event_wait(s, EV_RESET);
     }
     return CS_SUCCESS;
 } /* reset_card */
@@ -2071,9 +2432,7 @@ int pcmcia_suspend_card(client_handle_t 
 	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;
+    sm_event_wait(s, EV_SUSPEND);
 
     return CS_SUCCESS;
 } /* suspend_card */
@@ -2092,7 +2451,7 @@ int pcmcia_resume_card(client_handle_t h
 	return CS_IN_USE;
 
     DEBUG(1, "cs: waking up socket %d\n", i);
-    setup_socket(s);
+    sm_event_wait(s, EV_RESUME);
 
     return CS_SUCCESS;
 } /* resume_card */
@@ -2107,7 +2466,6 @@ int pcmcia_eject_card(client_handle_t ha
 {
     int i, ret;
     socket_info_t *s;
-    u_long flags;
     
     if (CHECK_HANDLE(handle))
 	return CS_BAD_HANDLE;
@@ -2121,9 +2479,7 @@ int pcmcia_eject_card(client_handle_t ha
     if (ret != 0)
 	return ret;
 
-    spin_lock_irqsave(&s->lock, flags);
-    do_shutdown(s);
-    spin_unlock_irqrestore(&s->lock, flags);
+    sm_event_wait(s, EV_EJECT);
     
     return CS_SUCCESS;
     
@@ -2131,9 +2487,8 @@ int pcmcia_eject_card(client_handle_t ha
 
 int pcmcia_insert_card(client_handle_t handle, client_req_t *req)
 {
-    int i, status;
+    int i;
     socket_info_t *s;
-    u_long flags;
     
     if (CHECK_HANDLE(handle))
 	return CS_BAD_HANDLE;
@@ -2143,17 +2498,9 @@ int pcmcia_insert_card(client_handle_t h
 
     DEBUG(1, "cs: user insert request on socket %d\n", i);
 
-    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);
+    sm_event_wait(s, EV_INSERT);
+    if (!(s->state & SOCKET_PRESENT))
+      return CS_NO_CARD;
 
     return CS_SUCCESS;
 } /* insert_card */
@@ -2433,6 +2780,10 @@ EXPORT_SYMBOL(pcmcia_socket_class);
 
 static int __init init_pcmcia_cs(void)
 {
+    pcmcia_wq = create_workqueue("pcmcia");
+    if (!pcmcia_wq)
+       return -ENOMEM;
+
     printk(KERN_INFO "%s\n", release);
     printk(KERN_INFO "  %s\n", options);
     DEBUG(0, "%s\n", version);
@@ -2454,6 +2805,8 @@ static void __exit exit_pcmcia_cs(void)
 #endif
     release_resource_db();
     devclass_unregister(&pcmcia_socket_class);
+
+    destroy_workqueue(pcmcia_wq);
 }
 
 subsys_initcall(init_pcmcia_cs);
diff -puN drivers/pcmcia/cs_internal.h~pcmcia-20030421 drivers/pcmcia/cs_internal.h
--- 25/drivers/pcmcia/cs_internal.h~pcmcia-20030421	Mon Apr 21 17:38:29 2003
+++ 25-akpm/drivers/pcmcia/cs_internal.h	Mon Apr 21 17:38:29 2003
@@ -118,9 +118,24 @@ typedef struct config_t {
 #define MAX_CIS_TABLE	64
 #define MAX_CIS_DATA	512
 
+struct socket_info_t;
+
+struct sm_state {
+	void			(*func)(struct socket_info_t *, unsigned int, unsigned int);
+	spinlock_t		lock;
+	unsigned int		event_pending;
+	unsigned int		event_mask;
+	unsigned int		event_ack;
+	unsigned int		tries;
+	wait_queue_head_t	wait_ack;
+	struct timer_list	timer;
+	struct work_struct	work;
+};
+
 typedef struct socket_info_t {
     spinlock_t			lock;
     struct pccard_operations *	ss_entry;
+    struct sm_state		sm;
     u_int			sock;
     socket_state_t		socket;
     socket_cap_t		cap;
@@ -216,6 +231,9 @@ int validate_cis(client_handle_t handle,
 int replace_cis(client_handle_t handle, cisdump_t *cis);
 int read_tuple(client_handle_t handle, cisdata_t code, void *parse);
 
+/* In cs.c */
+void pcmcia_resources_changed(void);
+
 /* In bulkmem.c */
 void retry_erase_list(struct erase_busy_t *list, u_int cause);
 int get_first_region(client_handle_t handle, region_info_t *rgn);
diff -puN drivers/pcmcia/rsrc_mgr.c~pcmcia-20030421 drivers/pcmcia/rsrc_mgr.c
--- 25/drivers/pcmcia/rsrc_mgr.c~pcmcia-20030421	Mon Apr 21 17:38:29 2003
+++ 25-akpm/drivers/pcmcia/rsrc_mgr.c	Mon Apr 21 17:38:29 2003
@@ -499,14 +499,16 @@ void validate_mem(socket_info_t *s)
 
 void validate_mem(socket_info_t *s)
 {
-    resource_map_t *m;
+    resource_map_t *m, *n;
     static int done = 0;
     
     if (probe_mem && done++ == 0) {
 	down(&rsrc_sem);
-	for (m = mem_db.next; m != &mem_db; m = m->next)
+	for (m = mem_db.next; m != &mem_db; m = n) {
+	    n = m->next;
 	    if (do_mem_probe(m->base, m->num, s))
 		break;
+	}
 	up(&rsrc_sem);
     }
 }
@@ -724,7 +726,7 @@ void undo_irq(u_int Attributes, int irq)
 static int adjust_memory(adjust_t *adj)
 {
     u_long base, num;
-    int i, ret;
+    int ret;
 
     base = adj->resource.memory.Base;
     num = adj->resource.memory.Size;
@@ -740,20 +742,14 @@ static int adjust_memory(adjust_t *adj)
 	break;
     case REMOVE_MANAGED_RESOURCE:
 	ret = sub_interval(&mem_db, base, num);
-	if (ret == CS_SUCCESS) {
-	    for (i = 0; i < sockets; i++) {
-		release_cis_mem(socket_table[i]);
-#ifdef CONFIG_CARDBUS
-		cb_release_cis_mem(socket_table[i]);
-#endif
-	    }
-	}
 	break;
     default:
 	ret = CS_UNSUPPORTED_FUNCTION;
     }
     up(&rsrc_sem);
-    
+    if (ret == CS_SUCCESS)
+	pcmcia_resources_changed();
+
     return ret;
 }
 
diff -puN include/pcmcia/bus_ops.h~pcmcia-20030421 include/pcmcia/bus_ops.h
--- 25/include/pcmcia/bus_ops.h~pcmcia-20030421	Mon Apr 21 17:38:29 2003
+++ 25-akpm/include/pcmcia/bus_ops.h	Mon Apr 21 17:38:29 2003
@@ -1,2 +0,0 @@
-/* now empty */
-#warning please remove the reference to this file
diff -puN include/pcmcia/driver_ops.h~pcmcia-20030421 include/pcmcia/driver_ops.h
--- 25/include/pcmcia/driver_ops.h~pcmcia-20030421	Mon Apr 21 17:38:29 2003
+++ 25-akpm/include/pcmcia/driver_ops.h	Mon Apr 21 17:38:29 2003
@@ -1,2 +0,0 @@
-/* now empty */
-#warning please remove the reference to this file
diff -puN include/pcmcia/ds.h~pcmcia-20030421 include/pcmcia/ds.h
--- 25/include/pcmcia/ds.h~pcmcia-20030421	Mon Apr 21 17:38:29 2003
+++ 25-akpm/include/pcmcia/ds.h	Mon Apr 21 17:38:29 2003
@@ -31,7 +31,6 @@
 #define _LINUX_DS_H
 
 #include <pcmcia/bulkmem.h>
-#include <linux/device.h>
 #include <pcmcia/cs_types.h>
 
 typedef struct tuple_parse_t {
@@ -107,6 +106,7 @@ typedef union ds_ioctl_arg_t {
 #define DS_BIND_MTD			_IOWR('d', 64, mtd_info_t)
 
 #ifdef __KERNEL__
+#include <linux/device.h>
 
 typedef struct dev_node_t {
     char		dev_name[DEV_NAME_LEN];

_
