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 /drivers | |
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pcmcia/ds.c | 92 |
1 files changed, 22 insertions, 70 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 | ||