
From: Dominik Brodowski <linux@dominikbrodowski.de>

Restructure unbind_request():
Before, unbind_request was called by cardmgr on the following occasions:

a) if the CS_EVENT_CARD_INSERTION event failed

b) during do_remove(), which is called on
	1) when cardmgr is informed of a CS_EVENT_CARD_REMOVAL event
	2) when cardmgr is informed of a CS_EVENT_EJECTION_REQUEST event, if
	   do_check() succeeds
	3) cardmgr exit (SIGINT/SIGTERM), if do_check() succeeds

We can ignore a), as the user is informed of the problem anyway, and can
take appropriate action then (eject the card, update config, write new driver,
insert card...).

b1) can be done directly, even before the userspace cardmgr is informed. 
    This speeds up the call to ->detach().

b2) All drivers I checked were based on the assumption that a
    CS_EVENT_CARD_REMOVAL event is received _first_, before a call to
    ->detach().  Most notably, some drivers issue first a call to their
    release() function [which else is called during EVENT_CARD_REMOVAL] during
    ->detach() if it hasn't been issued before.  So, it doesn't hurt if unbind
    is only called during the EVENT_CARD_REMOVAL step, and not during
    EJECTION_REQUEST.  The REMOVAL step is only called anyway if
    EJECTION_REQUEST succeeds, and the latter can only succeed if do_check()
    succeeds.

b3) If cardmgr exits from daemon mode, ds_release() is called.  I can't
    see a reason why this is good behaviour, especially as cards don't need
    cardmgr while running, only for setup.

Consequences:
- call unbind_request during CARD_REMOVAL handling, even before userspace
  is informed.
- return "0" if UNBIND_REQUEST is called from userspace.
- the driver's event handler is called with CARD_REMOVAL _always_ before
  ->detach() is called.

Signed-off-by: Dominik Brodowski <linux@brodo.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/pcmcia/ds.c |   59 +++++++++++++++++++++-----------------------
 1 files changed, 29 insertions(+), 30 deletions(-)

diff -puN drivers/pcmcia/ds.c~pcmcia-direct-ordered-unbind-of-devices drivers/pcmcia/ds.c
--- 25/drivers/pcmcia/ds.c~pcmcia-direct-ordered-unbind-of-devices	2004-12-03 18:44:42.108381752 -0800
+++ 25-akpm/drivers/pcmcia/ds.c	2004-12-03 18:44:42.113380992 -0800
@@ -110,6 +110,8 @@ static spinlock_t pcmcia_dev_list_lock;
 
 static int major_dev = -1;
 
+static int unbind_request(struct pcmcia_bus_socket *s);
+
 /*====================================================================*/
 
 /* code which was in cs.c before */
@@ -462,8 +464,6 @@ static int send_event(struct pcmcia_sock
 static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
 {
 	struct pcmcia_bus_socket *s = skt->pcmcia;
-	struct pcmcia_device *p_dev;
-	unsigned long flags;
 	int ret = 0;
 
 	ds_dbg(1, "ds_event(0x%06x, %d, 0x%p)\n",
@@ -474,11 +474,8 @@ static int ds_event(struct pcmcia_socket
 	case CS_EVENT_CARD_REMOVAL:
 		s->state &= ~DS_SOCKET_PRESENT;
 	    	send_event(skt, event, priority);
+		unbind_request(s);
 		handle_event(s, event);
-		spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
-		list_for_each_entry(p_dev, &s->devices_list, socket_device_list)
-			p_dev->client->state |= CLIENT_STALE;
-		spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
 		break;
 	
 	case CS_EVENT_CARD_INSERTION:
@@ -856,37 +853,39 @@ static int get_device_info(struct pcmcia
 
 /*====================================================================*/
 
-static int unbind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info)
+/* unbind _all_ devices attached to a given pcmcia_bus_socket. The
+ * drivers have been called with EVENT_CARD_REMOVAL before.
+ */
+static int unbind_request(struct pcmcia_bus_socket *s)
 {
 	struct pcmcia_device	*p_dev;
 	struct pcmcia_driver	*p_drv;
 	unsigned long		flags;
 
-	ds_dbg(2, "unbind_request(%d, '%s')\n", s->parent->sock,
-	       (char *)bind_info->dev_info);
+	ds_dbg(2, "unbind_request(%d)\n", s->parent->sock);
 
-	/* unregister the pcmcia_device */
-	spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
-	list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
-		if (p_dev->func == bind_info->function)
-			goto found;
-	}
-	spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
-	return 0;
-
- found:
-	list_del(&p_dev->socket_device_list);
-	spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+	for (;;) {
+		/* unregister all pcmcia_devices registered with this socket*/
+		spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+		if (list_empty(&s->devices_list)) {
+			spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ 			return 0;
+		}
+		p_dev = list_entry((&s->devices_list)->next, struct pcmcia_device, socket_device_list);
+		list_del(&p_dev->socket_device_list);
+		p_dev->client->state |= CLIENT_STALE;
+		spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
 
-	/* detach the "instance" */
-	p_drv = to_pcmcia_drv(p_dev->dev.driver);
-	if (p_drv) {
-		if ((p_drv->detach) && (p_dev->instance))
-			p_drv->detach(p_dev->instance);
-		module_put(p_drv->owner);
-	}
+		/* detach the "instance" */
+		p_drv = to_pcmcia_drv(p_dev->dev.driver);
+		if (p_drv) {
+			if ((p_drv->detach) && (p_dev->instance))
+				p_drv->detach(p_dev->instance);
+			module_put(p_drv->owner);
+		}
 
-	device_unregister(&p_dev->dev);
+		device_unregister(&p_dev->dev);
+  	}
 
 	return 0;
 } /* unbind_request */
@@ -1250,7 +1249,7 @@ static int ds_ioctl(struct inode * inode
 	err = get_device_info(s, &buf.bind_info, 0);
 	break;
     case DS_UNBIND_REQUEST:
-	err = unbind_request(s, &buf.bind_info);
+	err = 0;
 	break;
     case DS_BIND_MTD:
 	if (!capable(CAP_SYS_ADMIN)) return -EPERM;
_
