aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/pcmcia/Kconfig14
-rw-r--r--drivers/pcmcia/ds.c71
2 files changed, 81 insertions, 4 deletions
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index 14e4124e1523..22f7e8ca6584 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -58,6 +58,20 @@ config PCMCIA
58 58
59 If unsure, say Y. 59 If unsure, say Y.
60 60
61config PCMCIA_LOAD_CIS
62 bool "Load CIS updates from userspace (EXPERIMENTAL)"
63 depends on PCMCIA && EXPERIMENTAL
64 select FW_LOADER
65 default y
66 help
67 Some PCMCIA cards require an updated Card Information Structure (CIS)
68 to be loaded from userspace to work correctly. If you say Y here,
69 and your userspace is arranged correctly, this will be loaded
70 automatically using the in-kernel firmware loader and the hotplug
71 subsystem, instead of relying on cardmgr from pcmcia-cs to do so.
72
73 If unsure, say Y.
74
61config CARDBUS 75config CARDBUS
62 bool "32-bit CardBus support" 76 bool "32-bit CardBus support"
63 depends on PCI 77 depends on PCI
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
index 19b7dacdbe7a..5c88ff8e7d7e 100644
--- a/drivers/pcmcia/ds.c
+++ b/drivers/pcmcia/ds.c
@@ -36,6 +36,7 @@
36#include <linux/kref.h> 36#include <linux/kref.h>
37#include <linux/workqueue.h> 37#include <linux/workqueue.h>
38#include <linux/crc32.h> 38#include <linux/crc32.h>
39#include <linux/firmware.h>
39 40
40#include <asm/atomic.h> 41#include <asm/atomic.h>
41 42
@@ -295,6 +296,68 @@ static inline void pcmcia_check_driver(struct pcmcia_driver *p_drv) {
295} 296}
296#endif 297#endif
297 298
299
300#ifdef CONFIG_PCMCIA_LOAD_CIS
301
302/**
303 * pcmcia_load_firmware - load CIS from userspace if device-provided is broken
304 * @dev - the pcmcia device which needs a CIS override
305 * @filename - requested filename in /lib/firmware/cis/
306 *
307 * This uses the in-kernel firmware loading mechanism to use a "fake CIS" if
308 * the one provided by the card is broken. The firmware files reside in
309 * /lib/firmware/cis/ in userspace.
310 */
311static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
312{
313 struct pcmcia_socket *s = dev->socket;
314 const struct firmware *fw;
315 char path[20];
316 int ret=-ENOMEM;
317 cisdump_t *cis;
318
319 if (!filename)
320 return -EINVAL;
321
322 ds_dbg(1, "trying to load firmware %s\n", filename);
323
324 if (strlen(filename) > 14)
325 return -EINVAL;
326
327 snprintf(path, 20, "%s", filename);
328
329 if (request_firmware(&fw, path, &dev->dev) == 0) {
330 if (fw->size >= CISTPL_MAX_CIS_SIZE)
331 goto release;
332
333 cis = kmalloc(sizeof(cisdump_t), GFP_KERNEL);
334 if (!cis)
335 goto release;
336
337 memset(cis, 0, sizeof(cisdump_t));
338
339 cis->Length = fw->size + 1;
340 memcpy(cis->Data, fw->data, fw->size);
341
342 if (!pcmcia_replace_cis(s, cis))
343 ret = 0;
344 }
345 release:
346 release_firmware(fw);
347
348 return (ret);
349}
350
351#else /* !CONFIG_PCMCIA_LOAD_CIS */
352
353static inline int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
354{
355 return -ENODEV;
356}
357
358#endif
359
360
298/*======================================================================*/ 361/*======================================================================*/
299 362
300static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info); 363static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info);
@@ -739,11 +802,11 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev,
739 } 802 }
740 803
741 if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) { 804 if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) {
742 if (!dev->socket->fake_cis) { 805 if (!dev->socket->fake_cis)
743 /* FIXME: evaluate using firmware helpers to 806 pcmcia_load_firmware(dev, did->cisfile);
744 * automagically load it from userspace */ 807
808 if (!dev->socket->fake_cis)
745 return 0; 809 return 0;
746 }
747 } 810 }
748 811
749 if (did->match_flags & PCMCIA_DEV_ID_MATCH_ANONYMOUS) { 812 if (did->match_flags & PCMCIA_DEV_ID_MATCH_ANONYMOUS) {