Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 88b060d6 authored by Dominik Brodowski's avatar Dominik Brodowski
Browse files

pcmcia: improve check for same card in slot after resume



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@virtuousgeek.org>
CC: Linus Torvalds <torvalds@linux-foundation.org>
Tested-by: default avatarWolfram Sang <w.sang@pengutronix.de>
Signed-off-by: default avatarDominik Brodowski <linux@dominikbrodowski.net>
parent f131ddc4
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -377,6 +377,7 @@ int verify_cis_cache(struct pcmcia_socket *s)
	kfree(buf);
	kfree(buf);
	return 0;
	return 0;
}
}
EXPORT_SYMBOL(verify_cis_cache);


/*======================================================================
/*======================================================================


+30 −35
Original line number Original line Diff line number Diff line
@@ -328,7 +328,7 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority)
{
{
	int ret;
	int ret;


	if (s->state & SOCKET_CARDBUS)
	if ((s->state & SOCKET_CARDBUS) && (event != CS_EVENT_CARD_REMOVAL))
		return 0;
		return 0;


	dev_dbg(&s->dev, "send_event(event %d, pri %d, callback 0x%p)\n",
	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;
	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)
static int socket_reset(struct pcmcia_socket *skt)
{
{
	int status, i;
	int status, i;
@@ -395,7 +388,7 @@ static void socket_shutdown(struct pcmcia_socket *s)


	dev_dbg(&s->dev, "shutdown\n");
	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;
	s->state &= SOCKET_INUSE | SOCKET_PRESENT;
	msleep(shutdown_delay * 10);
	msleep(shutdown_delay * 10);
	s->state &= SOCKET_INUSE;
	s->state &= SOCKET_INUSE;
@@ -462,7 +455,8 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
			return -EINVAL;
			return -EINVAL;
		}
		}
		skt->state |= SOCKET_CARDBUS;
		skt->state |= SOCKET_CARDBUS;
	}
	} else
		skt->state &= ~SOCKET_CARDBUS;


	/*
	/*
	 * Decode the card voltage requirements, and apply power to the card.
	 * 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)
	if (skt->state & SOCKET_SUSPEND)
		return -EBUSY;
		return -EBUSY;


	skt->suspended_state = skt->state;

	send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
	send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
	skt->socket = dead_socket;
	skt->socket = dead_socket;
	skt->ops->set_socket(skt, &skt->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)
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);
		return socket_insert(skt);
	}


	if (skt->resume_status == 0) {
	if (skt->resume_status) {
		/*
		socket_shutdown(skt);
		 * FIXME: need a better check here for cardbus cards.
		return 0;
		 */
		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);
		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;
	return 0;
}
}


+15 −1
Original line number Original line Diff line number Diff line
@@ -1252,8 +1252,22 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
	case CS_EVENT_EJECTION_REQUEST:
	case CS_EVENT_EJECTION_REQUEST:
		break;
		break;


	case CS_EVENT_PM_SUSPEND:
	case CS_EVENT_PM_RESUME:
	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_RESET_PHYSICAL:
	case CS_EVENT_CARD_RESET:
	case CS_EVENT_CARD_RESET:
	default:
	default:
+1 −0
Original line number Original line Diff line number Diff line
@@ -137,6 +137,7 @@ struct pcmcia_socket {
	spinlock_t			lock;
	spinlock_t			lock;
	socket_state_t			socket;
	socket_state_t			socket;
	u_int				state;
	u_int				state;
	u_int				suspended_state;	/* state before suspend */
	u_short				functions;
	u_short				functions;
	u_short				lock_count;
	u_short				lock_count;
	pccard_mem_map			cis_mem;
	pccard_mem_map			cis_mem;