diff options
| author | Dominik Brodowski <linux@dominikbrodowski.net> | 2010-01-24 08:36:59 -0500 |
|---|---|---|
| committer | Dominik Brodowski <linux@dominikbrodowski.net> | 2010-02-17 11:48:27 -0500 |
| commit | aa584ca4cdd8db370a524c61fd3ca408303281e9 (patch) | |
| tree | dc7945721b8cdcaa88f4dc6d394c2b318c20504a | |
| parent | af461fc1875b6ec18e23b5f670af36c4ed35c84e (diff) | |
pcmcia: use state machine for extended requery
The requery callback now also handles the addition of a second pseudo
multifunction device. Avoids messing with dev_{g,s}et_drvdata(), and
fixes any workqueue <-> skt_mutex deadlock.
Tested-by: Wolfram Sang <w.sang@pengutronix.de>
Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
| -rw-r--r-- | drivers/pcmcia/ds.c | 92 | ||||
| -rw-r--r-- | include/pcmcia/ss.h | 11 |
2 files changed, 25 insertions, 78 deletions
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 5400e20c664e..9968c0d3a5fb 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c | |||
| @@ -244,23 +244,11 @@ static void pcmcia_release_dev(struct device *dev) | |||
| 244 | kfree(p_dev); | 244 | kfree(p_dev); |
| 245 | } | 245 | } |
| 246 | 246 | ||
| 247 | static void pcmcia_add_device_later(struct pcmcia_socket *s, int mfc) | ||
| 248 | { | ||
| 249 | if (!s->pcmcia_state.device_add_pending) { | ||
| 250 | dev_dbg(&s->dev, "scheduling to add %s secondary" | ||
| 251 | " device to %d\n", mfc ? "mfc" : "pfc", s->sock); | ||
| 252 | s->pcmcia_state.device_add_pending = 1; | ||
| 253 | s->pcmcia_state.mfc_pfc = mfc; | ||
| 254 | schedule_work(&s->device_add); | ||
| 255 | } | ||
| 256 | return; | ||
| 257 | } | ||
| 258 | 247 | ||
| 259 | static int pcmcia_device_probe(struct device *dev) | 248 | static int pcmcia_device_probe(struct device *dev) |
| 260 | { | 249 | { |
| 261 | struct pcmcia_device *p_dev; | 250 | struct pcmcia_device *p_dev; |
| 262 | struct pcmcia_driver *p_drv; | 251 | struct pcmcia_driver *p_drv; |
| 263 | struct pcmcia_device_id *did; | ||
| 264 | struct pcmcia_socket *s; | 252 | struct pcmcia_socket *s; |
| 265 | cistpl_config_t cis_config; | 253 | cistpl_config_t cis_config; |
| 266 | int ret = 0; | 254 | int ret = 0; |
| @@ -273,18 +261,6 @@ static int pcmcia_device_probe(struct device *dev) | |||
| 273 | p_drv = to_pcmcia_drv(dev->driver); | 261 | p_drv = to_pcmcia_drv(dev->driver); |
| 274 | s = p_dev->socket; | 262 | s = p_dev->socket; |
| 275 | 263 | ||
| 276 | /* The PCMCIA code passes the match data in via dev_set_drvdata(dev) | ||
| 277 | * which is an ugly hack. Once the driver probe is called it may | ||
| 278 | * and often will overwrite the match data so we must save it first | ||
| 279 | * | ||
| 280 | * handle pseudo multifunction devices: | ||
| 281 | * there are at most two pseudo multifunction devices. | ||
| 282 | * if we're matching against the first, schedule a | ||
| 283 | * call which will then check whether there are two | ||
| 284 | * pseudo devices, and if not, add the second one. | ||
| 285 | */ | ||
| 286 | did = dev_get_drvdata(&p_dev->dev); | ||
| 287 | |||
| 288 | dev_dbg(dev, "trying to bind to %s\n", p_drv->drv.name); | 264 | dev_dbg(dev, "trying to bind to %s\n", p_drv->drv.name); |
| 289 | 265 | ||
| 290 | if ((!p_drv->probe) || (!p_dev->function_config) || | 266 | if ((!p_drv->probe) || (!p_dev->function_config) || |
| @@ -314,9 +290,9 @@ static int pcmcia_device_probe(struct device *dev) | |||
| 314 | } | 290 | } |
| 315 | 291 | ||
| 316 | mutex_lock(&s->ops_mutex); | 292 | mutex_lock(&s->ops_mutex); |
| 317 | if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) && | 293 | if ((s->pcmcia_state.has_pfc) && |
| 318 | (p_dev->socket->device_count == 1) && (p_dev->device_no == 0)) | 294 | (p_dev->socket->device_count == 1) && (p_dev->device_no == 0)) |
| 319 | pcmcia_add_device_later(p_dev->socket, 0); | 295 | pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY); |
| 320 | mutex_unlock(&s->ops_mutex); | 296 | mutex_unlock(&s->ops_mutex); |
| 321 | 297 | ||
| 322 | put_module: | 298 | put_module: |
| @@ -369,7 +345,6 @@ static int pcmcia_device_remove(struct device *dev) | |||
| 369 | { | 345 | { |
| 370 | struct pcmcia_device *p_dev; | 346 | struct pcmcia_device *p_dev; |
| 371 | struct pcmcia_driver *p_drv; | 347 | struct pcmcia_driver *p_drv; |
| 372 | struct pcmcia_device_id *did; | ||
| 373 | int i; | 348 | int i; |
| 374 | 349 | ||
| 375 | p_dev = to_pcmcia_dev(dev); | 350 | p_dev = to_pcmcia_dev(dev); |
| @@ -381,8 +356,7 @@ static int pcmcia_device_remove(struct device *dev) | |||
| 381 | * pseudo multi-function card, we need to unbind | 356 | * pseudo multi-function card, we need to unbind |
| 382 | * all devices | 357 | * all devices |
| 383 | */ | 358 | */ |
| 384 | did = dev_get_drvdata(&p_dev->dev); | 359 | if ((p_dev->socket->pcmcia_state.has_pfc) && |
| 385 | if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) && | ||
| 386 | (p_dev->socket->device_count > 0) && | 360 | (p_dev->socket->device_count > 0) && |
| 387 | (p_dev->device_no == 0)) | 361 | (p_dev->device_no == 0)) |
| 388 | pcmcia_card_remove(p_dev->socket, p_dev); | 362 | pcmcia_card_remove(p_dev->socket, p_dev); |
| @@ -528,8 +502,8 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu | |||
| 528 | p_dev->device_no = (s->device_count++); | 502 | p_dev->device_no = (s->device_count++); |
| 529 | mutex_unlock(&s->ops_mutex); | 503 | mutex_unlock(&s->ops_mutex); |
| 530 | 504 | ||
| 531 | /* max of 4 devices per card */ | 505 | /* max of 2 devices per card */ |
| 532 | if (p_dev->device_no >= 4) | 506 | if (p_dev->device_no >= 2) |
| 533 | goto err_free; | 507 | goto err_free; |
| 534 | 508 | ||
| 535 | p_dev->socket = s; | 509 | p_dev->socket = s; |
| @@ -652,22 +626,6 @@ static int pcmcia_card_add(struct pcmcia_socket *s) | |||
| 652 | } | 626 | } |
| 653 | 627 | ||
| 654 | 628 | ||
| 655 | static void pcmcia_delayed_add_device(struct work_struct *work) | ||
| 656 | { | ||
| 657 | struct pcmcia_socket *s = | ||
| 658 | container_of(work, struct pcmcia_socket, device_add); | ||
| 659 | u8 mfc_pfc; | ||
| 660 | |||
| 661 | mutex_lock(&s->ops_mutex); | ||
| 662 | mfc_pfc = s->pcmcia_state.mfc_pfc; | ||
| 663 | s->pcmcia_state.device_add_pending = 0; | ||
| 664 | s->pcmcia_state.mfc_pfc = 0; | ||
| 665 | mutex_unlock(&s->ops_mutex); | ||
| 666 | |||
| 667 | dev_dbg(&s->dev, "adding additional device to %d\n", s->sock); | ||
| 668 | pcmcia_device_add(s, mfc_pfc); | ||
| 669 | } | ||
| 670 | |||
| 671 | static int pcmcia_requery_callback(struct device *dev, void * _data) | 629 | static int pcmcia_requery_callback(struct device *dev, void * _data) |
| 672 | { | 630 | { |
| 673 | struct pcmcia_device *p_dev = to_pcmcia_dev(dev); | 631 | struct pcmcia_device *p_dev = to_pcmcia_dev(dev); |
| @@ -679,9 +637,10 @@ static int pcmcia_requery_callback(struct device *dev, void * _data) | |||
| 679 | return 0; | 637 | return 0; |
| 680 | } | 638 | } |
| 681 | 639 | ||
| 640 | |||
| 682 | static void pcmcia_requery(struct pcmcia_socket *s) | 641 | static void pcmcia_requery(struct pcmcia_socket *s) |
| 683 | { | 642 | { |
| 684 | int present; | 643 | int present, has_pfc; |
| 685 | 644 | ||
| 686 | mutex_lock(&s->ops_mutex); | 645 | mutex_lock(&s->ops_mutex); |
| 687 | present = s->pcmcia_state.present; | 646 | present = s->pcmcia_state.present; |
| @@ -723,6 +682,15 @@ static void pcmcia_requery(struct pcmcia_socket *s) | |||
| 723 | } | 682 | } |
| 724 | } | 683 | } |
| 725 | 684 | ||
| 685 | /* If the PCMCIA device consists of two pseudo devices, | ||
| 686 | * call pcmcia_device_add() -- which will fail if both | ||
| 687 | * devices are already registered. */ | ||
| 688 | mutex_lock(&s->ops_mutex); | ||
| 689 | has_pfc = s->pcmcia_state.has_pfc; | ||
| 690 | mutex_unlock(&s->ops_mutex); | ||
| 691 | if (has_pfc) | ||
| 692 | pcmcia_device_add(s, 0); | ||
| 693 | |||
| 726 | /* we re-scan all devices, not just the ones connected to this | 694 | /* we re-scan all devices, not just the ones connected to this |
| 727 | * socket. This does not matter, though. */ | 695 | * socket. This does not matter, though. */ |
| 728 | if (bus_rescan_devices(&pcmcia_bus_type)) | 696 | if (bus_rescan_devices(&pcmcia_bus_type)) |
| @@ -746,9 +714,6 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) | |||
| 746 | struct pcmcia_socket *s = dev->socket; | 714 | struct pcmcia_socket *s = dev->socket; |
| 747 | const struct firmware *fw; | 715 | const struct firmware *fw; |
| 748 | int ret = -ENOMEM; | 716 | int ret = -ENOMEM; |
| 749 | int no_funcs; | ||
| 750 | int old_funcs; | ||
| 751 | cistpl_longlink_mfc_t mfc; | ||
| 752 | 717 | ||
| 753 | if (!filename) | 718 | if (!filename) |
| 754 | return -EINVAL; | 719 | return -EINVAL; |
| @@ -775,22 +740,8 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) | |||
| 775 | /* update information */ | 740 | /* update information */ |
| 776 | pcmcia_device_query(dev); | 741 | pcmcia_device_query(dev); |
| 777 | 742 | ||
| 778 | /* does this cis override add or remove functions? */ | 743 | /* requery (as number of functions might have changed) */ |
| 779 | old_funcs = s->functions; | 744 | pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY); |
| 780 | |||
| 781 | if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, &mfc)) | ||
| 782 | no_funcs = mfc.nfn; | ||
| 783 | else | ||
| 784 | no_funcs = 1; | ||
| 785 | s->functions = no_funcs; | ||
| 786 | |||
| 787 | if (old_funcs > no_funcs) | ||
| 788 | pcmcia_card_remove(s, dev); | ||
| 789 | else if (no_funcs > old_funcs) { | ||
| 790 | mutex_lock(&s->ops_mutex); | ||
| 791 | pcmcia_add_device_later(s, 1); | ||
| 792 | mutex_unlock(&s->ops_mutex); | ||
| 793 | } | ||
| 794 | } | 745 | } |
| 795 | release: | 746 | release: |
| 796 | release_firmware(fw); | 747 | release_firmware(fw); |
| @@ -857,6 +808,9 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev, | |||
| 857 | if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) { | 808 | if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) { |
| 858 | if (dev->device_no != did->device_no) | 809 | if (dev->device_no != did->device_no) |
| 859 | return 0; | 810 | return 0; |
| 811 | mutex_lock(&dev->socket->ops_mutex); | ||
| 812 | dev->socket->pcmcia_state.has_pfc = 1; | ||
| 813 | mutex_unlock(&dev->socket->ops_mutex); | ||
| 860 | } | 814 | } |
| 861 | 815 | ||
| 862 | if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID) { | 816 | if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID) { |
| @@ -905,8 +859,6 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev, | |||
| 905 | return 0; | 859 | return 0; |
| 906 | } | 860 | } |
| 907 | 861 | ||
| 908 | dev_set_drvdata(&dev->dev, did); | ||
| 909 | |||
| 910 | return 1; | 862 | return 1; |
| 911 | } | 863 | } |
| 912 | 864 | ||
| @@ -1300,6 +1252,7 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) | |||
| 1300 | 1252 | ||
| 1301 | case CS_EVENT_CARD_INSERTION: | 1253 | case CS_EVENT_CARD_INSERTION: |
| 1302 | mutex_lock(&s->ops_mutex); | 1254 | mutex_lock(&s->ops_mutex); |
| 1255 | s->pcmcia_state.has_pfc = 0; | ||
| 1303 | s->pcmcia_state.present = 1; | 1256 | s->pcmcia_state.present = 1; |
| 1304 | destroy_cis_cache(s); /* to be on the safe side... */ | 1257 | destroy_cis_cache(s); /* to be on the safe side... */ |
| 1305 | mutex_unlock(&s->ops_mutex); | 1258 | mutex_unlock(&s->ops_mutex); |
| @@ -1411,7 +1364,6 @@ static int __devinit pcmcia_bus_add_socket(struct device *dev, | |||
| 1411 | init_waitqueue_head(&socket->queue); | 1364 | init_waitqueue_head(&socket->queue); |
| 1412 | #endif | 1365 | #endif |
| 1413 | INIT_LIST_HEAD(&socket->devices_list); | 1366 | INIT_LIST_HEAD(&socket->devices_list); |
| 1414 | INIT_WORK(&socket->device_add, pcmcia_delayed_add_device); | ||
| 1415 | memset(&socket->pcmcia_state, 0, sizeof(u8)); | 1367 | memset(&socket->pcmcia_state, 0, sizeof(u8)); |
| 1416 | socket->device_count = 0; | 1368 | socket->device_count = 0; |
| 1417 | 1369 | ||
diff --git a/include/pcmcia/ss.h b/include/pcmcia/ss.h index ea5dec8c0d76..32896a773910 100644 --- a/include/pcmcia/ss.h +++ b/include/pcmcia/ss.h | |||
| @@ -230,18 +230,13 @@ struct pcmcia_socket { | |||
| 230 | u8 busy:1; | 230 | u8 busy:1; |
| 231 | /* pcmcia module is being unloaded */ | 231 | /* pcmcia module is being unloaded */ |
| 232 | u8 dead:1; | 232 | u8 dead:1; |
| 233 | /* a multifunction-device add event is pending */ | 233 | /* the PCMCIA card consists of two pseudo devices */ |
| 234 | u8 device_add_pending:1; | 234 | u8 has_pfc:1; |
| 235 | /* the pending event adds a mfc (1) or pfc (0) */ | ||
| 236 | u8 mfc_pfc:1; | ||
| 237 | 235 | ||
| 238 | u8 reserved:3; | 236 | u8 reserved:4; |
| 239 | } pcmcia_state; | 237 | } pcmcia_state; |
| 240 | 238 | ||
| 241 | 239 | ||
| 242 | /* for adding further pseudo-multifunction devices */ | ||
| 243 | struct work_struct device_add; | ||
| 244 | |||
| 245 | #ifdef CONFIG_PCMCIA_IOCTL | 240 | #ifdef CONFIG_PCMCIA_IOCTL |
| 246 | struct user_info_t *user; | 241 | struct user_info_t *user; |
| 247 | wait_queue_head_t queue; | 242 | wait_queue_head_t queue; |
