#include <linux/kref.h>
#include <linux/workqueue.h>
#include <linux/crc32.h>
+#include <linux/firmware.h>
#include <asm/atomic.h>
MODULE_LICENSE("GPL");
#ifdef DEBUG
-int ds_pc_debug;
+static int ds_pc_debug;
module_param_named(pc_debug, ds_pc_debug, int, 0644);
};
static spinlock_t pcmcia_dev_list_lock;
+static struct bus_type pcmcia_bus_type;
+
#define DS_SOCKET_PRESENT 0x01
#define DS_SOCKET_BUSY 0x02
#define DS_SOCKET_REMOVAL_PENDING 0x10
};
-int pcmcia_report_error(client_handle_t handle, error_info_t *err)
+static int pcmcia_report_error(client_handle_t handle, error_info_t *err)
{
int i;
char *serv;
return CS_SUCCESS;
} /* report_error */
-EXPORT_SYMBOL(pcmcia_report_error);
/* end of code which was in cs.c before */
}
EXPORT_SYMBOL(cs_error);
-#ifdef CONFIG_PCMCIA_DEBUG
-
static void pcmcia_check_driver(struct pcmcia_driver *p_drv)
{
"product string \"%s\": is 0x%x, should "
"be 0x%x\n", p_drv->drv.name, did->prod_id[i],
did->prod_id_hash[i], hash);
+ printk(KERN_DEBUG "pcmcia: see "
+ "Documentation/pcmcia/devicetable.txt for "
+ "details\n");
}
did++;
}
return;
}
-#else
-static inline void pcmcia_check_driver(struct pcmcia_driver *p_drv) {
- return;
+
+#ifdef CONFIG_PCMCIA_LOAD_CIS
+
+/**
+ * pcmcia_load_firmware - load CIS from userspace if device-provided is broken
+ * @dev - the pcmcia device which needs a CIS override
+ * @filename - requested filename in /lib/firmware/cis/
+ *
+ * This uses the in-kernel firmware loading mechanism to use a "fake CIS" if
+ * the one provided by the card is broken. The firmware files reside in
+ * /lib/firmware/cis/ in userspace.
+ */
+static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
+{
+ struct pcmcia_socket *s = dev->socket;
+ const struct firmware *fw;
+ char path[20];
+ int ret=-ENOMEM;
+ cisdump_t *cis;
+
+ if (!filename)
+ return -EINVAL;
+
+ ds_dbg(1, "trying to load firmware %s\n", filename);
+
+ if (strlen(filename) > 14)
+ return -EINVAL;
+
+ snprintf(path, 20, "%s", filename);
+
+ if (request_firmware(&fw, path, &dev->dev) == 0) {
+ if (fw->size >= CISTPL_MAX_CIS_SIZE)
+ goto release;
+
+ cis = kmalloc(sizeof(cisdump_t), GFP_KERNEL);
+ if (!cis)
+ goto release;
+
+ memset(cis, 0, sizeof(cisdump_t));
+
+ cis->Length = fw->size + 1;
+ memcpy(cis->Data, fw->data, fw->size);
+
+ if (!pcmcia_replace_cis(s, cis))
+ ret = 0;
+ }
+ release:
+ release_firmware(fw);
+
+ return (ret);
}
+
+#else /* !CONFIG_PCMCIA_LOAD_CIS */
+
+static inline int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
+{
+ return -ENODEV;
+}
+
#endif
+
/*======================================================================*/
static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info);
return;
}
-static void pcmcia_bus_rescan(void)
+static int pcmcia_requery(struct device *dev, void * _data)
{
+ struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
+ if (!p_dev->dev.driver)
+ pcmcia_device_query(p_dev);
+
+ return 0;
+}
+
+static void pcmcia_bus_rescan(struct pcmcia_socket *skt)
+{
+ int no_devices=0;
+ unsigned long flags;
+
/* must be called with skt_sem held */
+ spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+ if (list_empty(&skt->pcmcia->devices_list))
+ no_devices=1;
+ spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+
+ /* if no devices were added for this socket yet because of
+ * missing resource information or other trouble, we need to
+ * do this now. */
+ if (no_devices) {
+ int ret = pcmcia_card_add(skt);
+ if (ret)
+ return;
+ }
+
+ /* some device information might have changed because of a CIS
+ * update or because we can finally read it correctly... so
+ * determine it again, overwriting old values if necessary. */
+ bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery);
+
+ /* we re-scan all devices, not just the ones connected to this
+ * socket. This does not matter, though. */
bus_rescan_devices(&pcmcia_bus_type);
}
}
if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) {
- if (!dev->socket->fake_cis) {
- /* FIXME: evaluate using firmware helpers to
- * automagically load it from userspace */
+ if (!dev->socket->fake_cis)
+ pcmcia_load_firmware(dev, did->cisfile);
+
+ if (!dev->socket->fake_cis)
return 0;
- }
}
if (did->match_flags & PCMCIA_DEV_ID_MATCH_ANONYMOUS) {
/* Set up hotline to Card Services */
s->callback.owner = THIS_MODULE;
s->callback.event = &ds_event;
- s->callback.resources_done = &pcmcia_card_add;
- s->callback.replace_cis = &pcmcia_bus_rescan;
+ s->callback.requery = &pcmcia_bus_rescan;
socket->pcmcia = s;
ret = pccard_register_pcmcia(socket, &s->callback);
};
-struct bus_type pcmcia_bus_type = {
+static struct bus_type pcmcia_bus_type = {
.name = "pcmcia",
.hotplug = pcmcia_bus_hotplug,
.match = pcmcia_bus_match,
.dev_attrs = pcmcia_dev_attrs,
};
-EXPORT_SYMBOL(pcmcia_bus_type);
static int __init init_pcmcia_bus(void)