[PATCH 06/49] pcmcia: improve check for same card in slot after resume

Dominik Brodowski linux at dominikbrodowski.net
Sat Feb 27 05:42:01 EST 2010


During a suspend/resume cycle, an user may change the card in the
PCMCIA/CardBus slot. The pcmcia_core can at least look at the
socket state to check whether it is the same.

For PCMCIA devices, move the detection and handling of such a
change to ds.c.

For CardBus devices, the PCI hotplug interface doesn't offer a "rescan"
facility which also _removes_ devices no longer to be found behind a
bridge. Therefore, remove and re-add all devices unconditionally.

CC: Jesse Barnes <jbarnes at virtuousgeek.org>
CC: Linus Torvalds <torvalds at linux-foundation.org>
Tested-by: Wolfram Sang <w.sang at pengutronix.de>
Signed-off-by: Dominik Brodowski <linux at dominikbrodowski.net>
---
 drivers/pcmcia/cistpl.c |    1 +
 drivers/pcmcia/cs.c     |   65 +++++++++++++++++++++-------------------------
 drivers/pcmcia/ds.c     |   16 +++++++++++-
 include/pcmcia/ss.h     |    1 +
 4 files changed, 47 insertions(+), 36 deletions(-)

diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c
index 04bf1ba..a8323cb 100644
--- a/drivers/pcmcia/cistpl.c
+++ b/drivers/pcmcia/cistpl.c
@@ -377,6 +377,7 @@ int verify_cis_cache(struct pcmcia_socket *s)
 	kfree(buf);
 	return 0;
 }
+EXPORT_SYMBOL(verify_cis_cache);
 
 /*======================================================================
 
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
index 96d8d25..8c51493 100644
--- a/drivers/pcmcia/cs.c
+++ b/drivers/pcmcia/cs.c
@@ -328,7 +328,7 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority)
 {
 	int ret;
 
-	if (s->state & SOCKET_CARDBUS)
+	if ((s->state & SOCKET_CARDBUS) && (event != CS_EVENT_CARD_REMOVAL))
 		return 0;
 
 	dev_dbg(&s->dev, "send_event(event %d, pri %d, callback 0x%p)\n",
@@ -346,13 +346,6 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority)
 	return ret;
 }
 
-static void socket_remove_drivers(struct pcmcia_socket *skt)
-{
-	dev_dbg(&skt->dev, "remove_drivers\n");
-
-	send_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
-}
-
 static int socket_reset(struct pcmcia_socket *skt)
 {
 	int status, i;
@@ -395,7 +388,7 @@ static void socket_shutdown(struct pcmcia_socket *s)
 
 	dev_dbg(&s->dev, "shutdown\n");
 
-	socket_remove_drivers(s);
+	send_event(s, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
 	s->state &= SOCKET_INUSE | SOCKET_PRESENT;
 	msleep(shutdown_delay * 10);
 	s->state &= SOCKET_INUSE;
@@ -462,7 +455,8 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
 			return -EINVAL;
 		}
 		skt->state |= SOCKET_CARDBUS;
-	}
+	} else
+		skt->state &= ~SOCKET_CARDBUS;
 
 	/*
 	 * Decode the card voltage requirements, and apply power to the card.
@@ -544,6 +538,8 @@ static int socket_suspend(struct pcmcia_socket *skt)
 	if (skt->state & SOCKET_SUSPEND)
 		return -EBUSY;
 
+	skt->suspended_state = skt->state;
+
 	send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
 	skt->socket = dead_socket;
 	skt->ops->set_socket(skt, &skt->socket);
@@ -566,38 +562,37 @@ static int socket_early_resume(struct pcmcia_socket *skt)
 
 static int socket_late_resume(struct pcmcia_socket *skt)
 {
-	if (!(skt->state & SOCKET_PRESENT)) {
-		skt->state &= ~SOCKET_SUSPEND;
+	skt->state &= ~SOCKET_SUSPEND;
+
+	if (!(skt->state & SOCKET_PRESENT))
 		return socket_insert(skt);
+
+	if (skt->resume_status) {
+		socket_shutdown(skt);
+		return 0;
 	}
 
-	if (skt->resume_status == 0) {
-		/*
-		 * FIXME: need a better check here for cardbus cards.
-		 */
-		if (verify_cis_cache(skt) != 0) {
-			dev_dbg(&skt->dev, "cis mismatch - different card\n");
-			socket_remove_drivers(skt);
-			destroy_cis_cache(skt);
-			kfree(skt->fake_cis);
-			skt->fake_cis = NULL;
-			/*
-			 * Workaround: give DS time to schedule removal.
-			 * Remove me once the 100ms delay is eliminated
-			 * in ds.c
-			 */
-			msleep(200);
-			send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
-		} else {
-			dev_dbg(&skt->dev, "cis matches cache\n");
-			send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
-		}
-	} else {
+	if (skt->suspended_state != skt->state) {
+		dev_dbg(&skt->dev,
+			"suspend state 0x%x != resume state 0x%x\n",
+			skt->suspended_state, skt->state);
+
 		socket_shutdown(skt);
+		return socket_insert(skt);
 	}
 
-	skt->state &= ~SOCKET_SUSPEND;
+#ifdef CONFIG_CARDBUS
+	if (skt->state & SOCKET_CARDBUS) {
+		/* We can't be sure the CardBus card is the same
+		 * as the one previously inserted. Therefore, remove
+		 * and re-add... */
+		cb_free(skt);
+		cb_alloc(skt);
+		return 0;
+	}
+#endif
 
+	send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
 	return 0;
 }
 
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
index defa44c..87e0639 100644
--- a/drivers/pcmcia/ds.c
+++ b/drivers/pcmcia/ds.c
@@ -1252,8 +1252,22 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
 	case CS_EVENT_EJECTION_REQUEST:
 		break;
 
-	case CS_EVENT_PM_SUSPEND:
 	case CS_EVENT_PM_RESUME:
+		if (verify_cis_cache(skt) != 0) {
+			dev_dbg(&skt->dev, "cis mismatch - different card\n");
+			/* first, remove the card */
+			ds_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
+			destroy_cis_cache(skt);
+			kfree(skt->fake_cis);
+			skt->fake_cis = NULL;
+			/* now, add the new card */
+			ds_event(skt, CS_EVENT_CARD_INSERTION,
+				 CS_EVENT_PRI_LOW);
+		}
+		handle_event(skt, event);
+		break;
+
+	case CS_EVENT_PM_SUSPEND:
 	case CS_EVENT_RESET_PHYSICAL:
 	case CS_EVENT_CARD_RESET:
 	default:
diff --git a/include/pcmcia/ss.h b/include/pcmcia/ss.h
index cbfba88..b0ebd11 100644
--- a/include/pcmcia/ss.h
+++ b/include/pcmcia/ss.h
@@ -137,6 +137,7 @@ struct pcmcia_socket {
 	spinlock_t			lock;
 	socket_state_t			socket;
 	u_int				state;
+	u_int				suspended_state;	/* state before suspend */
 	u_short				functions;
 	u_short				lock_count;
 	pccard_mem_map			cis_mem;
-- 
1.6.3.3




More information about the linux-pcmcia mailing list