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: Wolfram Sang <w.sang@pengutronix.de> Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
This commit is contained in:
parent
f131ddc4bd
commit
88b060d6c0
4 changed files with 49 additions and 38 deletions
|
@ -377,6 +377,7 @@ int verify_cis_cache(struct pcmcia_socket *s)
|
|||
kfree(buf);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(verify_cis_cache);
|
||||
|
||||
/*======================================================================
|
||||
|
||||
|
|
|
@ -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->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);
|
||||
}
|
||||
|
||||
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 {
|
||||
socket_shutdown(skt);
|
||||
#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
|
||||
|
||||
skt->state &= ~SOCKET_SUSPEND;
|
||||
|
||||
send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue