[PATCH 30/33] pcmcia: pseudo device handling update

Dominik Brodowski linux at dominikbrodowski.net
Fri Mar 31 15:30:31 EST 2006


If the driver for the primary pseudo device is removed from the device,
the secondary driver must be removed as well -- it cannot exist on its own.

Signed-off-by: Dominik Brodowski <linux at dominikbrodowski.net>

---

 drivers/pcmcia/ds.c |   81 ++++++++++++++++++++++++++++++---------------------
 1 files changed, 48 insertions(+), 33 deletions(-)

d6ff5a8532b553e4d85ea65c99eaa0794edf5311
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
index 677105e..8966dd0 100644
--- a/drivers/pcmcia/ds.c
+++ b/drivers/pcmcia/ds.c
@@ -415,15 +415,61 @@ static int pcmcia_device_probe(struct de
 }
 
 
+/*
+ * Removes a PCMCIA card from the device tree and socket list.
+ */
+static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *leftover)
+{
+	struct pcmcia_device	*p_dev;
+	struct pcmcia_device	*tmp;
+	unsigned long		flags;
+
+	ds_dbg(2, "unbind_request(%d)\n", s->sock);
+
+
+	if (!leftover)
+		s->device_count = 0;
+	else
+		s->device_count = 1;
+
+	/* unregister all pcmcia_devices registered with this socket, except leftover */
+	list_for_each_entry_safe(p_dev, tmp, &s->devices_list, socket_device_list) {
+		if (p_dev == leftover)
+			continue;
+
+		spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+		list_del(&p_dev->socket_device_list);
+		p_dev->_removed=1;
+		spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+
+		device_unregister(&p_dev->dev);
+	}
+
+	return;
+}
+
+
 static int pcmcia_device_remove(struct device * dev)
 {
 	struct pcmcia_device *p_dev;
 	struct pcmcia_driver *p_drv;
+	struct pcmcia_device_id *did;
 	int i;
 
-	/* detach the "instance" */
 	p_dev = to_pcmcia_dev(dev);
 	p_drv = to_pcmcia_drv(dev->driver);
+
+	/* If we're removing the primary module driving a
+	 * pseudo multi-function card, we need to unbind
+	 * all devices
+	 */
+	did = (struct pcmcia_device_id *) p_dev->dev.driver_data;
+	if ((did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) &&
+	    (p_dev->socket->device_count != 0) &&
+	    (p_dev->device_no == 0))
+		pcmcia_card_remove(p_dev->socket, p_dev);
+
+	/* detach the "instance" */
 	if (!p_drv)
 		return 0;
 
@@ -449,37 +495,6 @@ static int pcmcia_device_remove(struct d
 
 
 /*
- * Removes a PCMCIA card from the device tree and socket list.
- */
-static void pcmcia_card_remove(struct pcmcia_socket *s)
-{
-	struct pcmcia_device	*p_dev;
-	unsigned long		flags;
-
-	ds_dbg(2, "unbind_request(%d)\n", s->sock);
-
-	s->device_count = 0;
-
-	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;
-		}
-		p_dev = list_entry((&s->devices_list)->next, struct pcmcia_device, socket_device_list);
-		list_del(&p_dev->socket_device_list);
-		p_dev->_removed=1;
-		spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
-
-		device_unregister(&p_dev->dev);
-	}
-
-	return;
-} /* unbind_request */
-
-
-/*
  * pcmcia_device_query -- determine information about a pcmcia device
  */
 static int pcmcia_device_query(struct pcmcia_device *p_dev)
@@ -1136,7 +1151,7 @@ static int ds_event(struct pcmcia_socket
 	switch (event) {
 	case CS_EVENT_CARD_REMOVAL:
 		s->pcmcia_state.present = 0;
-		pcmcia_card_remove(skt);
+		pcmcia_card_remove(skt, NULL);
 		handle_event(skt, event);
 		break;
 
-- 
1.2.4




More information about the linux-pcmcia mailing list