diff options
Diffstat (limited to 'drivers/pcmcia')
-rw-r--r-- | drivers/pcmcia/ds.c | 97 |
1 files changed, 62 insertions, 35 deletions
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 5223395b246a..32b4d6baa917 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c | |||
@@ -57,8 +57,6 @@ module_param_named(pc_debug, ds_pc_debug, int, 0644); | |||
57 | 57 | ||
58 | spinlock_t pcmcia_dev_list_lock; | 58 | spinlock_t pcmcia_dev_list_lock; |
59 | 59 | ||
60 | static int unbind_request(struct pcmcia_socket *s); | ||
61 | |||
62 | /*====================================================================*/ | 60 | /*====================================================================*/ |
63 | 61 | ||
64 | /* code which was in cs.c before */ | 62 | /* code which was in cs.c before */ |
@@ -205,7 +203,7 @@ static void pcmcia_check_driver(struct pcmcia_driver *p_drv) | |||
205 | unsigned int i; | 203 | unsigned int i; |
206 | u32 hash; | 204 | u32 hash; |
207 | 205 | ||
208 | if (!p_drv->attach || !p_drv->event || !p_drv->detach) | 206 | if (!p_drv->attach || !p_drv->event || !p_drv->remove) |
209 | printk(KERN_DEBUG "pcmcia: %s lacks a requisite callback " | 207 | printk(KERN_DEBUG "pcmcia: %s lacks a requisite callback " |
210 | "function\n", p_drv->drv.name); | 208 | "function\n", p_drv->drv.name); |
211 | 209 | ||
@@ -399,13 +397,42 @@ static int pcmcia_device_remove(struct device * dev) | |||
399 | { | 397 | { |
400 | struct pcmcia_device *p_dev; | 398 | struct pcmcia_device *p_dev; |
401 | struct pcmcia_driver *p_drv; | 399 | struct pcmcia_driver *p_drv; |
400 | int i; | ||
402 | 401 | ||
403 | /* detach the "instance" */ | 402 | /* detach the "instance" */ |
404 | p_dev = to_pcmcia_dev(dev); | 403 | p_dev = to_pcmcia_dev(dev); |
405 | p_drv = to_pcmcia_drv(dev->driver); | 404 | p_drv = to_pcmcia_drv(dev->driver); |
406 | 405 | ||
406 | /* the likely, new path */ | ||
407 | if (p_drv && p_drv->remove) { | ||
408 | p_drv->remove(p_dev); | ||
409 | |||
410 | /* check for proper unloading */ | ||
411 | if (p_dev->state & (CLIENT_IRQ_REQ|CLIENT_IO_REQ|CLIENT_CONFIG_LOCKED)) | ||
412 | printk(KERN_INFO "pcmcia: driver %s did not release config properly\n", | ||
413 | p_drv->drv.name); | ||
414 | |||
415 | for (i = 0; i < MAX_WIN; i++) | ||
416 | if (p_dev->state & CLIENT_WIN_REQ(i)) | ||
417 | printk(KERN_INFO "pcmcia: driver %s did not release windows properly\n", | ||
418 | p_drv->drv.name); | ||
419 | |||
420 | /* undo pcmcia_register_client */ | ||
421 | p_dev->state = CLIENT_UNBOUND; | ||
422 | pcmcia_put_dev(p_dev); | ||
423 | |||
424 | /* references from pcmcia_probe_device */ | ||
425 | pcmcia_put_dev(p_dev); | ||
426 | module_put(p_drv->owner); | ||
427 | |||
428 | return 0; | ||
429 | } | ||
430 | |||
431 | /* old path */ | ||
407 | if (p_drv) { | 432 | if (p_drv) { |
408 | if ((p_drv->detach) && (p_dev->instance)) { | 433 | if ((p_drv->detach) && (p_dev->instance)) { |
434 | printk(KERN_INFO "pcmcia: using deprecated detach mechanism. Fix the driver!\n"); | ||
435 | |||
409 | p_drv->detach(p_dev->instance); | 436 | p_drv->detach(p_dev->instance); |
410 | /* from pcmcia_probe_device */ | 437 | /* from pcmcia_probe_device */ |
411 | put_device(&p_dev->dev); | 438 | put_device(&p_dev->dev); |
@@ -417,6 +444,36 @@ static int pcmcia_device_remove(struct device * dev) | |||
417 | } | 444 | } |
418 | 445 | ||
419 | 446 | ||
447 | /* | ||
448 | * Removes a PCMCIA card from the device tree and socket list. | ||
449 | */ | ||
450 | static void pcmcia_card_remove(struct pcmcia_socket *s) | ||
451 | { | ||
452 | struct pcmcia_device *p_dev; | ||
453 | unsigned long flags; | ||
454 | |||
455 | ds_dbg(2, "unbind_request(%d)\n", s->sock); | ||
456 | |||
457 | s->device_count = 0; | ||
458 | |||
459 | for (;;) { | ||
460 | /* unregister all pcmcia_devices registered with this socket*/ | ||
461 | spin_lock_irqsave(&pcmcia_dev_list_lock, flags); | ||
462 | if (list_empty(&s->devices_list)) { | ||
463 | spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); | ||
464 | return; | ||
465 | } | ||
466 | p_dev = list_entry((&s->devices_list)->next, struct pcmcia_device, socket_device_list); | ||
467 | list_del(&p_dev->socket_device_list); | ||
468 | p_dev->state |= CLIENT_STALE; | ||
469 | spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); | ||
470 | |||
471 | device_unregister(&p_dev->dev); | ||
472 | } | ||
473 | |||
474 | return; | ||
475 | } /* unbind_request */ | ||
476 | |||
420 | 477 | ||
421 | /* | 478 | /* |
422 | * pcmcia_device_query -- determine information about a pcmcia device | 479 | * pcmcia_device_query -- determine information about a pcmcia device |
@@ -1059,8 +1116,8 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) | |||
1059 | 1116 | ||
1060 | case CS_EVENT_CARD_REMOVAL: | 1117 | case CS_EVENT_CARD_REMOVAL: |
1061 | s->pcmcia_state.present = 0; | 1118 | s->pcmcia_state.present = 0; |
1062 | send_event(skt, event, priority); | 1119 | send_event(skt, event, priority); |
1063 | unbind_request(skt); | 1120 | pcmcia_card_remove(skt); |
1064 | handle_event(skt, event); | 1121 | handle_event(skt, event); |
1065 | break; | 1122 | break; |
1066 | 1123 | ||
@@ -1177,36 +1234,6 @@ int pcmcia_register_client(struct pcmcia_device **handle, client_reg_t *req) | |||
1177 | EXPORT_SYMBOL(pcmcia_register_client); | 1234 | EXPORT_SYMBOL(pcmcia_register_client); |
1178 | 1235 | ||
1179 | 1236 | ||
1180 | /* unbind _all_ devices attached to a given pcmcia_bus_socket. The | ||
1181 | * drivers have been called with EVENT_CARD_REMOVAL before. | ||
1182 | */ | ||
1183 | static int unbind_request(struct pcmcia_socket *s) | ||
1184 | { | ||
1185 | struct pcmcia_device *p_dev; | ||
1186 | unsigned long flags; | ||
1187 | |||
1188 | ds_dbg(2, "unbind_request(%d)\n", s->sock); | ||
1189 | |||
1190 | s->device_count = 0; | ||
1191 | |||
1192 | for (;;) { | ||
1193 | /* unregister all pcmcia_devices registered with this socket*/ | ||
1194 | spin_lock_irqsave(&pcmcia_dev_list_lock, flags); | ||
1195 | if (list_empty(&s->devices_list)) { | ||
1196 | spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); | ||
1197 | return 0; | ||
1198 | } | ||
1199 | p_dev = list_entry((&s->devices_list)->next, struct pcmcia_device, socket_device_list); | ||
1200 | list_del(&p_dev->socket_device_list); | ||
1201 | p_dev->state |= CLIENT_STALE; | ||
1202 | spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); | ||
1203 | |||
1204 | device_unregister(&p_dev->dev); | ||
1205 | } | ||
1206 | |||
1207 | return 0; | ||
1208 | } /* unbind_request */ | ||
1209 | |||
1210 | int pcmcia_deregister_client(struct pcmcia_device *p_dev) | 1237 | int pcmcia_deregister_client(struct pcmcia_device *p_dev) |
1211 | { | 1238 | { |
1212 | struct pcmcia_socket *s; | 1239 | struct pcmcia_socket *s; |