Skip to content

Commit

Permalink
pcmcia: improve check for same card in slot after resume
Browse files Browse the repository at this point in the history
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 <[email protected]>
CC: Linus Torvalds <[email protected]>
Tested-by: Wolfram Sang <[email protected]>
Signed-off-by: Dominik Brodowski <[email protected]>
  • Loading branch information
Dominik Brodowski committed Jan 17, 2010
1 parent f131ddc commit 88b060d
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 36 deletions.
1 change: 1 addition & 0 deletions drivers/pcmcia/cistpl.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ int verify_cis_cache(struct pcmcia_socket *s)
kfree(buf);
return 0;
}
EXPORT_SYMBOL(verify_cis_cache);

/*======================================================================
Expand Down
65 changes: 30 additions & 35 deletions drivers/pcmcia/cs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}

Expand Down
16 changes: 15 additions & 1 deletion drivers/pcmcia/ds.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions include/pcmcia/ss.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 88b060d

Please sign in to comment.