diff options
author | Dominik Brodowski <linux@dominikbrodowski.net> | 2006-11-06 21:52:16 -0500 |
---|---|---|
committer | Dominik Brodowski <linux@dominikbrodowski.net> | 2006-12-04 20:09:15 -0500 |
commit | 1d2c90425d5b0dcbf4a0fab2053d5087758b76a0 (patch) | |
tree | 7c8aaa01b60e81843a3ca4124ccd9ea916fca145 /drivers/pcmcia/ds.c | |
parent | 3e022d0c77e159a59d3ebfc44ad76a05202c2a6b (diff) |
[PATCH] pcmcia: multifunction card handling fixes
s->functions needs to be initialized earlier, for the "let's see
how high it increases" approach means that pcmcia_request_irq()
(which makes use of this value) is confused, and might request
an exclusive IRQ first even though it is not supposed to.
Also, a CIS override autoloaded using the firmware loader may
allow for the use of more or less functions in a multifunction
card. Therefore, we may need to schedule a call to add this
second function later on, or simply remove the other function
(it's always the first -valid- function which reaches this
codepath).
Many thanks to Fabrice Bellet for debugging and testing patches.
Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
Diffstat (limited to 'drivers/pcmcia/ds.c')
-rw-r--r-- | drivers/pcmcia/ds.c | 153 |
1 files changed, 87 insertions, 66 deletions
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 7f6e94cd067a..da7ceb523b48 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c | |||
@@ -231,65 +231,6 @@ static void pcmcia_check_driver(struct pcmcia_driver *p_drv) | |||
231 | } | 231 | } |
232 | 232 | ||
233 | 233 | ||
234 | #ifdef CONFIG_PCMCIA_LOAD_CIS | ||
235 | |||
236 | /** | ||
237 | * pcmcia_load_firmware - load CIS from userspace if device-provided is broken | ||
238 | * @dev - the pcmcia device which needs a CIS override | ||
239 | * @filename - requested filename in /lib/firmware/ | ||
240 | * | ||
241 | * This uses the in-kernel firmware loading mechanism to use a "fake CIS" if | ||
242 | * the one provided by the card is broken. The firmware files reside in | ||
243 | * /lib/firmware/ in userspace. | ||
244 | */ | ||
245 | static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) | ||
246 | { | ||
247 | struct pcmcia_socket *s = dev->socket; | ||
248 | const struct firmware *fw; | ||
249 | char path[20]; | ||
250 | int ret=-ENOMEM; | ||
251 | cisdump_t *cis; | ||
252 | |||
253 | if (!filename) | ||
254 | return -EINVAL; | ||
255 | |||
256 | ds_dbg(1, "trying to load firmware %s\n", filename); | ||
257 | |||
258 | if (strlen(filename) > 14) | ||
259 | return -EINVAL; | ||
260 | |||
261 | snprintf(path, 20, "%s", filename); | ||
262 | |||
263 | if (request_firmware(&fw, path, &dev->dev) == 0) { | ||
264 | if (fw->size >= CISTPL_MAX_CIS_SIZE) | ||
265 | goto release; | ||
266 | |||
267 | cis = kzalloc(sizeof(cisdump_t), GFP_KERNEL); | ||
268 | if (!cis) | ||
269 | goto release; | ||
270 | |||
271 | cis->Length = fw->size + 1; | ||
272 | memcpy(cis->Data, fw->data, fw->size); | ||
273 | |||
274 | if (!pcmcia_replace_cis(s, cis)) | ||
275 | ret = 0; | ||
276 | } | ||
277 | release: | ||
278 | release_firmware(fw); | ||
279 | |||
280 | return (ret); | ||
281 | } | ||
282 | |||
283 | #else /* !CONFIG_PCMCIA_LOAD_CIS */ | ||
284 | |||
285 | static inline int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) | ||
286 | { | ||
287 | return -ENODEV; | ||
288 | } | ||
289 | |||
290 | #endif | ||
291 | |||
292 | |||
293 | /*======================================================================*/ | 234 | /*======================================================================*/ |
294 | 235 | ||
295 | 236 | ||
@@ -356,10 +297,11 @@ static void pcmcia_release_dev(struct device *dev) | |||
356 | kfree(p_dev); | 297 | kfree(p_dev); |
357 | } | 298 | } |
358 | 299 | ||
359 | static void pcmcia_add_pseudo_device(struct pcmcia_socket *s) | 300 | static void pcmcia_add_device_later(struct pcmcia_socket *s, int mfc) |
360 | { | 301 | { |
361 | if (!s->pcmcia_state.device_add_pending) { | 302 | if (!s->pcmcia_state.device_add_pending) { |
362 | s->pcmcia_state.device_add_pending = 1; | 303 | s->pcmcia_state.device_add_pending = 1; |
304 | s->pcmcia_state.mfc_pfc = mfc; | ||
363 | schedule_work(&s->device_add); | 305 | schedule_work(&s->device_add); |
364 | } | 306 | } |
365 | return; | 307 | return; |
@@ -400,7 +342,7 @@ static int pcmcia_device_probe(struct device * dev) | |||
400 | did = p_dev->dev.driver_data; | 342 | did = p_dev->dev.driver_data; |
401 | if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) && | 343 | if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) && |
402 | (p_dev->socket->device_count == 1) && (p_dev->device_no == 0)) | 344 | (p_dev->socket->device_count == 1) && (p_dev->device_no == 0)) |
403 | pcmcia_add_pseudo_device(p_dev->socket); | 345 | pcmcia_add_device_later(p_dev->socket, 0); |
404 | 346 | ||
405 | put_module: | 347 | put_module: |
406 | if (ret) | 348 | if (ret) |
@@ -598,8 +540,6 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f | |||
598 | p_dev->socket = s; | 540 | p_dev->socket = s; |
599 | p_dev->device_no = (s->device_count++); | 541 | p_dev->device_no = (s->device_count++); |
600 | p_dev->func = function; | 542 | p_dev->func = function; |
601 | if (s->functions <= function) | ||
602 | s->functions = function + 1; | ||
603 | 543 | ||
604 | p_dev->dev.bus = &pcmcia_bus_type; | 544 | p_dev->dev.bus = &pcmcia_bus_type; |
605 | p_dev->dev.parent = s->dev.dev; | 545 | p_dev->dev.parent = s->dev.dev; |
@@ -690,6 +630,7 @@ static int pcmcia_card_add(struct pcmcia_socket *s) | |||
690 | no_funcs = mfc.nfn; | 630 | no_funcs = mfc.nfn; |
691 | else | 631 | else |
692 | no_funcs = 1; | 632 | no_funcs = 1; |
633 | s->functions = no_funcs; | ||
693 | 634 | ||
694 | for (i=0; i < no_funcs; i++) | 635 | for (i=0; i < no_funcs; i++) |
695 | pcmcia_device_add(s, i); | 636 | pcmcia_device_add(s, i); |
@@ -698,11 +639,12 @@ static int pcmcia_card_add(struct pcmcia_socket *s) | |||
698 | } | 639 | } |
699 | 640 | ||
700 | 641 | ||
701 | static void pcmcia_delayed_add_pseudo_device(void *data) | 642 | static void pcmcia_delayed_add_device(void *data) |
702 | { | 643 | { |
703 | struct pcmcia_socket *s = data; | 644 | struct pcmcia_socket *s = data; |
704 | pcmcia_device_add(s, 0); | 645 | pcmcia_device_add(s, s->pcmcia_state.mfc_pfc); |
705 | s->pcmcia_state.device_add_pending = 0; | 646 | s->pcmcia_state.device_add_pending = 0; |
647 | s->pcmcia_state.mfc_pfc = 0; | ||
706 | } | 648 | } |
707 | 649 | ||
708 | static int pcmcia_requery(struct device *dev, void * _data) | 650 | static int pcmcia_requery(struct device *dev, void * _data) |
@@ -751,6 +693,85 @@ static void pcmcia_bus_rescan(struct pcmcia_socket *skt, int new_cis) | |||
751 | printk(KERN_INFO "pcmcia: bus_rescan_devices failed\n"); | 693 | printk(KERN_INFO "pcmcia: bus_rescan_devices failed\n"); |
752 | } | 694 | } |
753 | 695 | ||
696 | #ifdef CONFIG_PCMCIA_LOAD_CIS | ||
697 | |||
698 | /** | ||
699 | * pcmcia_load_firmware - load CIS from userspace if device-provided is broken | ||
700 | * @dev - the pcmcia device which needs a CIS override | ||
701 | * @filename - requested filename in /lib/firmware/ | ||
702 | * | ||
703 | * This uses the in-kernel firmware loading mechanism to use a "fake CIS" if | ||
704 | * the one provided by the card is broken. The firmware files reside in | ||
705 | * /lib/firmware/ in userspace. | ||
706 | */ | ||
707 | static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) | ||
708 | { | ||
709 | struct pcmcia_socket *s = dev->socket; | ||
710 | const struct firmware *fw; | ||
711 | char path[20]; | ||
712 | int ret = -ENOMEM; | ||
713 | int no_funcs; | ||
714 | int old_funcs; | ||
715 | cisdump_t *cis; | ||
716 | cistpl_longlink_mfc_t mfc; | ||
717 | |||
718 | if (!filename) | ||
719 | return -EINVAL; | ||
720 | |||
721 | ds_dbg(1, "trying to load firmware %s\n", filename); | ||
722 | |||
723 | if (strlen(filename) > 14) | ||
724 | return -EINVAL; | ||
725 | |||
726 | snprintf(path, 20, "%s", filename); | ||
727 | |||
728 | if (request_firmware(&fw, path, &dev->dev) == 0) { | ||
729 | if (fw->size >= CISTPL_MAX_CIS_SIZE) | ||
730 | goto release; | ||
731 | |||
732 | cis = kzalloc(sizeof(cisdump_t), GFP_KERNEL); | ||
733 | if (!cis) | ||
734 | goto release; | ||
735 | |||
736 | cis->Length = fw->size + 1; | ||
737 | memcpy(cis->Data, fw->data, fw->size); | ||
738 | |||
739 | if (!pcmcia_replace_cis(s, cis)) | ||
740 | ret = 0; | ||
741 | |||
742 | /* update information */ | ||
743 | pcmcia_device_query(dev); | ||
744 | |||
745 | /* does this cis override add or remove functions? */ | ||
746 | old_funcs = s->functions; | ||
747 | |||
748 | if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, &mfc)) | ||
749 | no_funcs = mfc.nfn; | ||
750 | else | ||
751 | no_funcs = 1; | ||
752 | s->functions = no_funcs; | ||
753 | |||
754 | if (old_funcs > no_funcs) | ||
755 | pcmcia_card_remove(s, dev); | ||
756 | else if (no_funcs > old_funcs) | ||
757 | pcmcia_add_device_later(s, 1); | ||
758 | } | ||
759 | release: | ||
760 | release_firmware(fw); | ||
761 | |||
762 | return (ret); | ||
763 | } | ||
764 | |||
765 | #else /* !CONFIG_PCMCIA_LOAD_CIS */ | ||
766 | |||
767 | static inline int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) | ||
768 | { | ||
769 | return -ENODEV; | ||
770 | } | ||
771 | |||
772 | #endif | ||
773 | |||
774 | |||
754 | static inline int pcmcia_devmatch(struct pcmcia_device *dev, | 775 | static inline int pcmcia_devmatch(struct pcmcia_device *dev, |
755 | struct pcmcia_device_id *did) | 776 | struct pcmcia_device_id *did) |
756 | { | 777 | { |
@@ -1250,7 +1271,7 @@ static int __devinit pcmcia_bus_add_socket(struct class_device *class_dev, | |||
1250 | init_waitqueue_head(&socket->queue); | 1271 | init_waitqueue_head(&socket->queue); |
1251 | #endif | 1272 | #endif |
1252 | INIT_LIST_HEAD(&socket->devices_list); | 1273 | INIT_LIST_HEAD(&socket->devices_list); |
1253 | INIT_WORK(&socket->device_add, pcmcia_delayed_add_pseudo_device, socket); | 1274 | INIT_WORK(&socket->device_add, pcmcia_delayed_add_device, socket); |
1254 | memset(&socket->pcmcia_state, 0, sizeof(u8)); | 1275 | memset(&socket->pcmcia_state, 0, sizeof(u8)); |
1255 | socket->device_count = 0; | 1276 | socket->device_count = 0; |
1256 | 1277 | ||