[PATCH] pcmcia: add some Documentation
[powerpc.git] / drivers / pcmcia / ds.c
index 19b7dac..2c3c3da 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/kref.h>
 #include <linux/workqueue.h>
 #include <linux/crc32.h>
+#include <linux/firmware.h>
 
 #include <asm/atomic.h>
 
@@ -59,7 +60,7 @@ MODULE_DESCRIPTION("PCMCIA Driver Services");
 MODULE_LICENSE("GPL");
 
 #ifdef DEBUG
-int ds_pc_debug;
+static int ds_pc_debug;
 
 module_param_named(pc_debug, ds_pc_debug, int, 0644);
 
@@ -107,6 +108,8 @@ struct pcmcia_bus_socket {
 };
 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
@@ -217,7 +220,7 @@ static const lookup_t service_table[] = {
 };
 
 
-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;
@@ -247,7 +250,6 @@ int pcmcia_report_error(client_handle_t handle, error_info_t *err)
 
        return CS_SUCCESS;
 } /* report_error */
-EXPORT_SYMBOL(pcmcia_report_error);
 
 /* end of code which was in cs.c before */
 
@@ -260,8 +262,6 @@ void cs_error(client_handle_t handle, int func, int ret)
 }
 EXPORT_SYMBOL(cs_error);
 
-#ifdef CONFIG_PCMCIA_DEBUG
-
 
 static void pcmcia_check_driver(struct pcmcia_driver *p_drv)
 {
@@ -282,6 +282,9 @@ 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++;
        }
@@ -289,12 +292,68 @@ static void pcmcia_check_driver(struct pcmcia_driver *p_drv)
        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);
@@ -653,9 +712,42 @@ static inline void pcmcia_add_pseudo_device(struct pcmcia_bus_socket *s)
        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);
 }
 
@@ -739,11 +831,11 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev,
        }
 
        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) {
@@ -1797,8 +1889,7 @@ static int __devinit pcmcia_bus_add_socket(struct class_device *class_dev)
        /* 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);
@@ -1838,13 +1929,12 @@ static struct class_interface pcmcia_bus_interface = {
 };
 
 
-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)