* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
* (C) 1999 David A. Hinds
- * (C) 2003 - 2005 Dominik Brodowski
+ * (C) 2003 - 2006 Dominik Brodowski
*/
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/workqueue.h>
#include <linux/crc32.h>
#include <linux/firmware.h>
+#include <linux/kref.h>
+#include <linux/dma-mapping.h>
-#define IN_CARD_SERVICES
#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ss.h>
#include "cs_internal.h"
-#include "ds_internal.h"
/*====================================================================*/
MODULE_DESCRIPTION("PCMCIA Driver Services");
MODULE_LICENSE("GPL");
-#ifdef DEBUG
+#ifdef CONFIG_PCMCIA_DEBUG
int ds_pc_debug;
module_param_named(pc_debug, ds_pc_debug, int, 0644);
#define ds_dbg(lvl, fmt, arg...) do { \
- if (ds_pc_debug > (lvl)) \
+ if (ds_pc_debug > (lvl)) \
printk(KERN_DEBUG "ds: " fmt , ## arg); \
} while (0)
+#define ds_dev_dbg(lvl, dev, fmt, arg...) do { \
+ if (ds_pc_debug > (lvl)) \
+ dev_printk(KERN_DEBUG, dev, "ds: " fmt , ## arg); \
+} while (0)
#else
#define ds_dbg(lvl, fmt, arg...) do { } while (0)
+#define ds_dev_dbg(lvl, dev, fmt, arg...) do { } while (0)
#endif
spinlock_t pcmcia_dev_list_lock;
/* String tables for error messages */
typedef struct lookup_t {
- int key;
- char *msg;
+ const int key;
+ const char *msg;
} lookup_t;
static const lookup_t error_table[] = {
- { CS_SUCCESS, "Operation succeeded" },
- { CS_BAD_ADAPTER, "Bad adapter" },
- { CS_BAD_ATTRIBUTE, "Bad attribute", },
- { CS_BAD_BASE, "Bad base address" },
- { CS_BAD_EDC, "Bad EDC" },
- { CS_BAD_IRQ, "Bad IRQ" },
- { CS_BAD_OFFSET, "Bad offset" },
- { CS_BAD_PAGE, "Bad page number" },
- { CS_READ_FAILURE, "Read failure" },
- { CS_BAD_SIZE, "Bad size" },
- { CS_BAD_SOCKET, "Bad socket" },
- { CS_BAD_TYPE, "Bad type" },
- { CS_BAD_VCC, "Bad Vcc" },
- { CS_BAD_VPP, "Bad Vpp" },
- { CS_BAD_WINDOW, "Bad window" },
- { CS_WRITE_FAILURE, "Write failure" },
- { CS_NO_CARD, "No card present" },
- { CS_UNSUPPORTED_FUNCTION, "Usupported function" },
- { CS_UNSUPPORTED_MODE, "Unsupported mode" },
- { CS_BAD_SPEED, "Bad speed" },
- { CS_BUSY, "Resource busy" },
- { CS_GENERAL_FAILURE, "General failure" },
- { CS_WRITE_PROTECTED, "Write protected" },
- { CS_BAD_ARG_LENGTH, "Bad argument length" },
- { CS_BAD_ARGS, "Bad arguments" },
- { CS_CONFIGURATION_LOCKED, "Configuration locked" },
- { CS_IN_USE, "Resource in use" },
- { CS_NO_MORE_ITEMS, "No more items" },
- { CS_OUT_OF_RESOURCE, "Out of resource" },
- { CS_BAD_HANDLE, "Bad handle" },
- { CS_BAD_TUPLE, "Bad CIS tuple" }
+ { 0, "Operation succeeded" },
+ { -EIO, "Input/Output error" },
+ { -ENODEV, "No card present" },
+ { -EINVAL, "Bad parameter" },
+ { -EACCES, "Configuration locked" },
+ { -EBUSY, "Resource in use" },
+ { -ENOSPC, "No more items" },
+ { -ENOMEM, "Out of resource" },
};
{ ReplaceCIS, "ReplaceCIS" }
};
-
-static int pcmcia_report_error(struct pcmcia_device *p_dev, error_info_t *err)
+const char *pcmcia_error_func(int func)
{
int i;
- char *serv;
-
- if (!p_dev)
- printk(KERN_NOTICE);
- else
- printk(KERN_NOTICE "%s: ", p_dev->dev.bus_id);
for (i = 0; i < ARRAY_SIZE(service_table); i++)
- if (service_table[i].key == err->func)
- break;
- if (i < ARRAY_SIZE(service_table))
- serv = service_table[i].msg;
- else
- serv = "Unknown service number";
+ if (service_table[i].key == func)
+ return service_table[i].msg;
- for (i = 0; i < ARRAY_SIZE(error_table); i++)
- if (error_table[i].key == err->retcode)
- break;
- if (i < ARRAY_SIZE(error_table))
- printk("%s: %s\n", serv, error_table[i].msg);
- else
- printk("%s: Unknown error code %#x\n", serv, err->retcode);
+ return "Unknown service number";
+}
+EXPORT_SYMBOL(pcmcia_error_func);
- return CS_SUCCESS;
-} /* report_error */
+const char *pcmcia_error_ret(int ret)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(error_table); i++)
+ if (error_table[i].key == ret)
+ return error_table[i].msg;
-/* end of code which was in cs.c before */
+ return "unknown";
+}
+EXPORT_SYMBOL(pcmcia_error_ret);
/*======================================================================*/
-void cs_error(struct pcmcia_device *p_dev, int func, int ret)
-{
- error_info_t err = { func, ret };
- pcmcia_report_error(p_dev, &err);
-}
-EXPORT_SYMBOL(cs_error);
static void pcmcia_check_driver(struct pcmcia_driver *p_drv)
unsigned int i;
u32 hash;
- if (!p_drv->attach || !p_drv->event || !p_drv->remove)
+ if (!p_drv->probe || !p_drv->remove)
printk(KERN_DEBUG "pcmcia: %s lacks a requisite callback "
"function\n", p_drv->drv.name);
}
-#ifdef CONFIG_PCMCIA_LOAD_CIS
+/*======================================================================*/
+
+
+struct pcmcia_dynid {
+ struct list_head node;
+ struct pcmcia_device_id id;
+};
/**
- * 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/
+ * pcmcia_store_new_id - add a new PCMCIA device ID to this driver and re-probe devices
+ * @driver: target device driver
+ * @buf: buffer for scanning device ID data
+ * @count: input size
*
- * 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.
+ * Adds a new dynamic PCMCIA device ID to this driver,
+ * and causes the driver to probe for all devices again.
*/
-static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
+static ssize_t
+pcmcia_store_new_id(struct device_driver *driver, const char *buf, size_t count)
{
- struct pcmcia_socket *s = dev->socket;
- const struct firmware *fw;
- char path[20];
- int ret=-ENOMEM;
- cisdump_t *cis;
-
- if (!filename)
+ struct pcmcia_dynid *dynid;
+ struct pcmcia_driver *pdrv = to_pcmcia_drv(driver);
+ __u16 match_flags, manf_id, card_id;
+ __u8 func_id, function, device_no;
+ __u32 prod_id_hash[4] = {0, 0, 0, 0};
+ int fields=0;
+ int retval = 0;
+
+ fields = sscanf(buf, "%hx %hx %hx %hhx %hhx %hhx %x %x %x %x",
+ &match_flags, &manf_id, &card_id, &func_id, &function, &device_no,
+ &prod_id_hash[0], &prod_id_hash[1], &prod_id_hash[2], &prod_id_hash[3]);
+ if (fields < 6)
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;
+ dynid = kzalloc(sizeof(struct pcmcia_dynid), GFP_KERNEL);
+ if (!dynid)
+ return -ENOMEM;
- cis = kmalloc(sizeof(cisdump_t), GFP_KERNEL);
- if (!cis)
- goto release;
+ INIT_LIST_HEAD(&dynid->node);
+ dynid->id.match_flags = match_flags;
+ dynid->id.manf_id = manf_id;
+ dynid->id.card_id = card_id;
+ dynid->id.func_id = func_id;
+ dynid->id.function = function;
+ dynid->id.device_no = device_no;
+ memcpy(dynid->id.prod_id_hash, prod_id_hash, sizeof(__u32) * 4);
+
+ spin_lock(&pdrv->dynids.lock);
+ list_add_tail(&pdrv->dynids.list, &dynid->node);
+ spin_unlock(&pdrv->dynids.lock);
+
+ if (get_driver(&pdrv->drv)) {
+ retval = driver_attach(&pdrv->drv);
+ put_driver(&pdrv->drv);
+ }
- memset(cis, 0, sizeof(cisdump_t));
+ if (retval)
+ return retval;
+ return count;
+}
+static DRIVER_ATTR(new_id, S_IWUSR, NULL, pcmcia_store_new_id);
- cis->Length = fw->size + 1;
- memcpy(cis->Data, fw->data, fw->size);
+static void
+pcmcia_free_dynids(struct pcmcia_driver *drv)
+{
+ struct pcmcia_dynid *dynid, *n;
- if (!pcmcia_replace_cis(s, cis))
- ret = 0;
+ spin_lock(&drv->dynids.lock);
+ list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) {
+ list_del(&dynid->node);
+ kfree(dynid);
}
- release:
- release_firmware(fw);
-
- return (ret);
+ spin_unlock(&drv->dynids.lock);
}
-#else /* !CONFIG_PCMCIA_LOAD_CIS */
-
-static inline int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
+static int
+pcmcia_create_newid_file(struct pcmcia_driver *drv)
{
- return -ENODEV;
+ int error = 0;
+ if (drv->probe != NULL)
+ error = driver_create_file(&drv->drv, &driver_attr_new_id);
+ return error;
}
-#endif
-
-
-/*======================================================================*/
-
/**
* pcmcia_register_driver - register a PCMCIA driver with the bus core
+ * @driver: the &driver being registered
*
* Registers a PCMCIA driver with the PCMCIA bus core.
*/
-static int pcmcia_device_probe(struct device *dev);
-static int pcmcia_device_remove(struct device * dev);
-
int pcmcia_register_driver(struct pcmcia_driver *driver)
{
+ int error;
+
if (!driver)
return -EINVAL;
/* initialize common fields */
driver->drv.bus = &pcmcia_bus_type;
driver->drv.owner = driver->owner;
- driver->drv.probe = pcmcia_device_probe;
- driver->drv.remove = pcmcia_device_remove;
+ spin_lock_init(&driver->dynids.lock);
+ INIT_LIST_HEAD(&driver->dynids.list);
+
+ ds_dbg(3, "registering driver %s\n", driver->drv.name);
+
+ error = driver_register(&driver->drv);
+ if (error < 0)
+ return error;
- return driver_register(&driver->drv);
+ error = pcmcia_create_newid_file(driver);
+ if (error)
+ driver_unregister(&driver->drv);
+
+ return error;
}
EXPORT_SYMBOL(pcmcia_register_driver);
/**
* pcmcia_unregister_driver - unregister a PCMCIA driver with the bus core
+ * @driver: the &driver being unregistered
*/
void pcmcia_unregister_driver(struct pcmcia_driver *driver)
{
+ ds_dbg(3, "unregistering driver %s\n", driver->drv.name);
driver_unregister(&driver->drv);
+ pcmcia_free_dynids(driver);
}
EXPORT_SYMBOL(pcmcia_unregister_driver);
put_device(&p_dev->dev);
}
+static void pcmcia_release_function(struct kref *ref)
+{
+ struct config_t *c = container_of(ref, struct config_t, ref);
+ ds_dbg(1, "releasing config_t\n");
+ kfree(c);
+}
+
static void pcmcia_release_dev(struct device *dev)
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
- ds_dbg(1, "releasing dev %p\n", p_dev);
+ ds_dev_dbg(1, dev, "releasing device\n");
pcmcia_put_socket(p_dev->socket);
kfree(p_dev->devname);
+ kref_put(&p_dev->function_config->ref, pcmcia_release_function);
kfree(p_dev);
}
+static void pcmcia_add_device_later(struct pcmcia_socket *s, int mfc)
+{
+ if (!s->pcmcia_state.device_add_pending) {
+ ds_dev_dbg(1, &s->dev, "scheduling to add %s secondary"
+ " device to %d\n", mfc ? "mfc" : "pfc", s->sock);
+ s->pcmcia_state.device_add_pending = 1;
+ s->pcmcia_state.mfc_pfc = mfc;
+ schedule_work(&s->device_add);
+ }
+ return;
+}
static int pcmcia_device_probe(struct device * dev)
{
struct pcmcia_device *p_dev;
struct pcmcia_driver *p_drv;
+ struct pcmcia_device_id *did;
+ struct pcmcia_socket *s;
+ cistpl_config_t cis_config;
int ret = 0;
dev = get_device(dev);
p_dev = to_pcmcia_dev(dev);
p_drv = to_pcmcia_drv(dev->driver);
+ s = p_dev->socket;
+
+ /* The PCMCIA code passes the match data in via dev_set_drvdata(dev)
+ * which is an ugly hack. Once the driver probe is called it may
+ * and often will overwrite the match data so we must save it first
+ *
+ * handle pseudo multifunction devices:
+ * there are at most two pseudo multifunction devices.
+ * if we're matching against the first, schedule a
+ * call which will then check whether there are two
+ * pseudo devices, and if not, add the second one.
+ */
+ did = dev_get_drvdata(&p_dev->dev);
+
+ ds_dev_dbg(1, dev, "trying to bind to %s\n", p_drv->drv.name);
- if (!try_module_get(p_drv->owner)) {
+ if ((!p_drv->probe) || (!p_dev->function_config) ||
+ (!try_module_get(p_drv->owner))) {
ret = -EINVAL;
goto put_dev;
}
- if (p_drv->attach) {
- p_dev->instance = p_drv->attach();
- if ((!p_dev->instance) || (p_dev->state & CLIENT_UNBOUND)) {
- printk(KERN_NOTICE "ds: unable to create instance "
- "of '%s'!\n", p_drv->drv.name);
- ret = -EINVAL;
- }
+ /* set up some more device information */
+ ret = pccard_read_tuple(p_dev->socket, p_dev->func, CISTPL_CONFIG,
+ &cis_config);
+ if (!ret) {
+ p_dev->conf.ConfigBase = cis_config.base;
+ p_dev->conf.Present = cis_config.rmask[0];
+ } else {
+ dev_printk(KERN_INFO, dev,
+ "pcmcia: could not parse base and rmask0 of CIS\n");
+ p_dev->conf.ConfigBase = 0;
+ p_dev->conf.Present = 0;
}
+ ret = p_drv->probe(p_dev);
+ if (ret) {
+ ds_dev_dbg(1, dev, "binding to %s failed with %d\n",
+ p_drv->drv.name, ret);
+ goto put_module;
+ }
+
+ if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) &&
+ (p_dev->socket->device_count == 1) && (p_dev->device_no == 0))
+ pcmcia_add_device_later(p_dev->socket, 0);
+
+put_module:
if (ret)
module_put(p_drv->owner);
- put_dev:
- if ((ret) || !(p_drv->attach))
+put_dev:
+ if (ret)
put_device(dev);
return (ret);
}
+/*
+ * Removes a PCMCIA card from the device tree and socket list.
+ */
+static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *leftover)
+{
+ struct pcmcia_device *p_dev;
+ struct pcmcia_device *tmp;
+ unsigned long flags;
+
+ ds_dev_dbg(2, leftover ? &leftover->dev : &s->dev,
+ "pcmcia_card_remove(%d) %s\n", s->sock,
+ leftover ? leftover->devname : "");
+
+ if (!leftover)
+ s->device_count = 0;
+ else
+ s->device_count = 1;
+
+ /* unregister all pcmcia_devices registered with this socket, except leftover */
+ list_for_each_entry_safe(p_dev, tmp, &s->devices_list, socket_device_list) {
+ if (p_dev == leftover)
+ continue;
+
+ spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+ list_del(&p_dev->socket_device_list);
+ p_dev->_removed=1;
+ spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+
+ ds_dev_dbg(2, &p_dev->dev, "unregistering device\n");
+ device_unregister(&p_dev->dev);
+ }
+
+ return;
+}
+
static int pcmcia_device_remove(struct device * dev)
{
struct pcmcia_device *p_dev;
struct pcmcia_driver *p_drv;
+ struct pcmcia_device_id *did;
int i;
- /* detach the "instance" */
p_dev = to_pcmcia_dev(dev);
p_drv = to_pcmcia_drv(dev->driver);
+
+ ds_dev_dbg(1, dev, "removing device\n");
+
+ /* If we're removing the primary module driving a
+ * pseudo multi-function card, we need to unbind
+ * all devices
+ */
+ did = dev_get_drvdata(&p_dev->dev);
+ if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) &&
+ (p_dev->socket->device_count != 0) &&
+ (p_dev->device_no == 0))
+ pcmcia_card_remove(p_dev->socket, p_dev);
+
+ /* detach the "instance" */
if (!p_drv)
return 0;
if (p_drv->remove)
p_drv->remove(p_dev);
+ p_dev->dev_node = NULL;
+
/* check for proper unloading */
- if (p_dev->state & (CLIENT_IRQ_REQ|CLIENT_IO_REQ|CLIENT_CONFIG_LOCKED))
- printk(KERN_INFO "pcmcia: driver %s did not release config properly\n",
- p_drv->drv.name);
+ if (p_dev->_irq || p_dev->_io || p_dev->_locked)
+ dev_printk(KERN_INFO, dev,
+ "pcmcia: driver %s did not release config properly\n",
+ p_drv->drv.name);
for (i = 0; i < MAX_WIN; i++)
- if (p_dev->state & CLIENT_WIN_REQ(i))
- printk(KERN_INFO "pcmcia: driver %s did not release windows properly\n",
- p_drv->drv.name);
-
- /* undo pcmcia_register_client */
- p_dev->state = CLIENT_UNBOUND;
- pcmcia_put_dev(p_dev);
+ if (p_dev->_win & CLIENT_WIN_REQ(i))
+ dev_printk(KERN_INFO, dev,
+ "pcmcia: driver %s did not release window properly\n",
+ p_drv->drv.name);
/* references from pcmcia_probe_device */
pcmcia_put_dev(p_dev);
/*
- * Removes a PCMCIA card from the device tree and socket list.
- */
-static void pcmcia_card_remove(struct pcmcia_socket *s)
-{
- struct pcmcia_device *p_dev;
- unsigned long flags;
-
- ds_dbg(2, "unbind_request(%d)\n", s->sock);
-
- s->device_count = 0;
-
- for (;;) {
- /* unregister all pcmcia_devices registered with this socket*/
- spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
- if (list_empty(&s->devices_list)) {
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
- return;
- }
- p_dev = list_entry((&s->devices_list)->next, struct pcmcia_device, socket_device_list);
- list_del(&p_dev->socket_device_list);
- p_dev->state |= CLIENT_STALE;
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
-
- device_unregister(&p_dev->dev);
- }
-
- return;
-} /* unbind_request */
-
-
-/*
* pcmcia_device_query -- determine information about a pcmcia device
*/
static int pcmcia_device_query(struct pcmcia_device *p_dev)
}
if (!pccard_read_tuple(p_dev->socket, p_dev->func,
CISTPL_DEVICE_GEO, devgeo)) {
- ds_dbg(0, "mem device geometry probably means "
- "FUNCID_MEMORY\n");
+ ds_dev_dbg(0, &p_dev->dev,
+ "mem device geometry probably means "
+ "FUNCID_MEMORY\n");
p_dev->func_id = CISTPL_FUNCID_MEMORY;
p_dev->has_func_id = 1;
}
tmp = vers1->str + vers1->ofs[i];
length = strlen(tmp) + 1;
- if ((length < 3) || (length > 255))
+ if ((length < 2) || (length > 255))
continue;
p_dev->prod_id[i] = kmalloc(sizeof(char) * length,
* won't work, this doesn't matter much at the moment: the driver core doesn't
* support it either.
*/
-static DECLARE_MUTEX(device_add_lock);
+static DEFINE_MUTEX(device_add_lock);
struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function)
{
- struct pcmcia_device *p_dev;
+ struct pcmcia_device *p_dev, *tmp_dev;
unsigned long flags;
- int bus_id_len;
s = pcmcia_get_socket(s);
if (!s)
return NULL;
- down(&device_add_lock);
+ mutex_lock(&device_add_lock);
+
+ ds_dbg(3, "adding device to %d, function %d\n", s->sock, function);
- /* max of 2 devices per card */
- if (s->device_count == 2)
+ /* max of 4 devices per card */
+ if (s->device_count == 4)
goto err_put;
- p_dev = kmalloc(sizeof(struct pcmcia_device), GFP_KERNEL);
+ p_dev = kzalloc(sizeof(struct pcmcia_device), GFP_KERNEL);
if (!p_dev)
goto err_put;
- memset(p_dev, 0, sizeof(struct pcmcia_device));
p_dev->socket = s;
p_dev->device_no = (s->device_count++);
p_dev->func = function;
p_dev->dev.bus = &pcmcia_bus_type;
- p_dev->dev.parent = s->dev.dev;
+ p_dev->dev.parent = s->dev.parent;
p_dev->dev.release = pcmcia_release_dev;
- bus_id_len = sprintf (p_dev->dev.bus_id, "%d.%d", p_dev->socket->sock, p_dev->device_no);
-
- p_dev->devname = kmalloc(6 + bus_id_len + 1, GFP_KERNEL);
+ /* by default don't allow DMA */
+ p_dev->dma_mask = DMA_MASK_NONE;
+ p_dev->dev.dma_mask = &p_dev->dma_mask;
+ dev_set_name(&p_dev->dev, "%d.%d", p_dev->socket->sock, p_dev->device_no);
+ if (!dev_name(&p_dev->dev))
+ goto err_free;
+ p_dev->devname = kasprintf(GFP_KERNEL, "pcmcia%s", dev_name(&p_dev->dev));
if (!p_dev->devname)
goto err_free;
- sprintf (p_dev->devname, "pcmcia%s", p_dev->dev.bus_id);
+ ds_dev_dbg(3, &p_dev->dev, "devname is %s\n", p_dev->devname);
+
+ spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
- /* compat */
- p_dev->state = CLIENT_UNBOUND;
+ /*
+ * p_dev->function_config must be the same for all card functions.
+ * Note that this is serialized by the device_add_lock, so that
+ * only one such struct will be created.
+ */
+ list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list)
+ if (p_dev->func == tmp_dev->func) {
+ p_dev->function_config = tmp_dev->function_config;
+ p_dev->io = tmp_dev->io;
+ p_dev->irq = tmp_dev->irq;
+ kref_get(&p_dev->function_config->ref);
+ }
/* Add to the list in pcmcia_bus_socket */
- spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
- list_add_tail(&p_dev->socket_device_list, &s->devices_list);
+ list_add(&p_dev->socket_device_list, &s->devices_list);
+
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
- printk(KERN_NOTICE "pcmcia: registering new device %s\n",
- p_dev->devname);
+ if (!p_dev->function_config) {
+ ds_dev_dbg(3, &p_dev->dev, "creating config_t\n");
+ p_dev->function_config = kzalloc(sizeof(struct config_t),
+ GFP_KERNEL);
+ if (!p_dev->function_config)
+ goto err_unreg;
+ kref_init(&p_dev->function_config->ref);
+ }
- pcmcia_device_query(p_dev);
+ dev_printk(KERN_NOTICE, &p_dev->dev,
+ "pcmcia: registering new device %s\n",
+ p_dev->devname);
- if (device_register(&p_dev->dev)) {
- spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
- list_del(&p_dev->socket_device_list);
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ pcmcia_device_query(p_dev);
- goto err_free;
- }
+ if (device_register(&p_dev->dev))
+ goto err_unreg;
- up(&device_add_lock);
+ mutex_unlock(&device_add_lock);
return p_dev;
+ err_unreg:
+ spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+ list_del(&p_dev->socket_device_list);
+ spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+
err_free:
kfree(p_dev->devname);
kfree(p_dev);
s->device_count--;
err_put:
- up(&device_add_lock);
+ mutex_unlock(&device_add_lock);
pcmcia_put_socket(s);
return NULL;
static int pcmcia_card_add(struct pcmcia_socket *s)
{
- cisinfo_t cisinfo;
cistpl_longlink_mfc_t mfc;
- unsigned int no_funcs, i;
+ unsigned int no_funcs, i, no_chains;
int ret = 0;
- if (!(s->resource_setup_done))
+ if (!(s->resource_setup_done)) {
+ ds_dev_dbg(3, &s->dev,
+ "no resources available, delaying card_add\n");
return -EAGAIN; /* try again, but later... */
+ }
- if (pcmcia_validate_mem(s))
+ if (pcmcia_validate_mem(s)) {
+ ds_dev_dbg(3, &s->dev, "validating mem resources failed, "
+ "delaying card_add\n");
return -EAGAIN; /* try again, but later... */
+ }
- ret = pccard_validate_cis(s, BIND_FN_ALL, &cisinfo);
- if (ret || !cisinfo.Chains) {
- ds_dbg(0, "invalid CIS or invalid resources\n");
+ ret = pccard_validate_cis(s, BIND_FN_ALL, &no_chains);
+ if (ret || !no_chains) {
+ ds_dev_dbg(0, &s->dev, "invalid CIS or invalid resources\n");
return -ENODEV;
}
no_funcs = mfc.nfn;
else
no_funcs = 1;
+ s->functions = no_funcs;
for (i=0; i < no_funcs; i++)
pcmcia_device_add(s, i);
}
-static void pcmcia_delayed_add_pseudo_device(void *data)
+static void pcmcia_delayed_add_device(struct work_struct *work)
{
- struct pcmcia_socket *s = data;
- pcmcia_device_add(s, 0);
+ struct pcmcia_socket *s =
+ container_of(work, struct pcmcia_socket, device_add);
+ ds_dev_dbg(1, &s->dev, "adding additional device to %d\n", s->sock);
+ pcmcia_device_add(s, s->pcmcia_state.mfc_pfc);
s->pcmcia_state.device_add_pending = 0;
-}
-
-static inline void pcmcia_add_pseudo_device(struct pcmcia_socket *s)
-{
- if (!s->pcmcia_state.device_add_pending) {
- s->pcmcia_state.device_add_pending = 1;
- schedule_work(&s->device_add);
- }
- return;
+ s->pcmcia_state.mfc_pfc = 0;
}
static int pcmcia_requery(struct device *dev, void * _data)
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
- if (!p_dev->dev.driver)
+ if (!p_dev->dev.driver) {
+ ds_dev_dbg(1, dev, "update device information\n");
pcmcia_device_query(p_dev);
+ }
return 0;
}
-static void pcmcia_bus_rescan(struct pcmcia_socket *skt)
+static void pcmcia_bus_rescan(struct pcmcia_socket *skt, int new_cis)
{
- int no_devices=0;
+ int no_devices = 0;
+ int ret = 0;
unsigned long flags;
- /* must be called with skt_sem held */
+ /* must be called with skt_mutex held */
+ ds_dev_dbg(0, &skt->dev, "re-scanning socket %d\n", skt->sock);
+
spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
if (list_empty(&skt->devices_list))
- no_devices=1;
+ no_devices = 1;
spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+ /* If this is because of a CIS override, start over */
+ if (new_cis && !no_devices)
+ pcmcia_card_remove(skt, NULL);
+
/* 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 (no_devices || new_cis) {
+ ret = pcmcia_card_add(skt);
if (ret)
return;
}
/* we re-scan all devices, not just the ones connected to this
* socket. This does not matter, though. */
- bus_rescan_devices(&pcmcia_bus_type);
+ ret = bus_rescan_devices(&pcmcia_bus_type);
+ if (ret)
+ printk(KERN_INFO "pcmcia: bus_rescan_devices failed\n");
+}
+
+#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/
+ *
+ * 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/ in userspace.
+ */
+static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
+{
+ struct pcmcia_socket *s = dev->socket;
+ const struct firmware *fw;
+ int ret = -ENOMEM;
+ int no_funcs;
+ int old_funcs;
+ cistpl_longlink_mfc_t mfc;
+
+ if (!filename)
+ return -EINVAL;
+
+ ds_dev_dbg(1, &dev->dev, "trying to load CIS file %s\n", filename);
+
+ if (request_firmware(&fw, filename, &dev->dev) == 0) {
+ if (fw->size >= CISTPL_MAX_CIS_SIZE) {
+ ret = -EINVAL;
+ dev_printk(KERN_ERR, &dev->dev,
+ "pcmcia: CIS override is too big\n");
+ goto release;
+ }
+
+ if (!pcmcia_replace_cis(s, fw->data, fw->size))
+ ret = 0;
+ else {
+ dev_printk(KERN_ERR, &dev->dev,
+ "pcmcia: CIS override failed\n");
+ goto release;
+ }
+
+
+ /* update information */
+ pcmcia_device_query(dev);
+
+ /* does this cis override add or remove functions? */
+ old_funcs = s->functions;
+
+ if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, &mfc))
+ no_funcs = mfc.nfn;
+ else
+ no_funcs = 1;
+ s->functions = no_funcs;
+
+ if (old_funcs > no_funcs)
+ pcmcia_card_remove(s, dev);
+ else if (no_funcs > old_funcs)
+ pcmcia_add_device_later(s, 1);
+ }
+ 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 inline int pcmcia_devmatch(struct pcmcia_device *dev,
struct pcmcia_device_id *did)
{
}
if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) {
- /* handle pseudo multifunction devices:
- * there are at most two pseudo multifunction devices.
- * if we're matching against the first, schedule a
- * call which will then check whether there are two
- * pseudo devices, and if not, add the second one.
- */
- if (dev->device_no == 0)
- pcmcia_add_pseudo_device(dev->socket);
-
if (dev->device_no != did->device_no)
return 0;
}
* after it has re-checked that there is no possible module
* with a prod_id/manf_id/card_id match.
*/
+ ds_dev_dbg(0, &dev->dev,
+ "skipping FUNC_ID match until userspace interaction\n");
if (!dev->allow_func_id_match)
return 0;
}
if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) {
+ ds_dev_dbg(0, &dev->dev, "device needs a fake CIS\n");
if (!dev->socket->fake_cis)
pcmcia_load_firmware(dev, did->cisfile);
return 0;
}
- dev->dev.driver_data = (void *) did;
+ dev_set_drvdata(&dev->dev, did);
return 1;
}
struct pcmcia_device * p_dev = to_pcmcia_dev(dev);
struct pcmcia_driver * p_drv = to_pcmcia_drv(drv);
struct pcmcia_device_id *did = p_drv->id_table;
+ struct pcmcia_dynid *dynid;
+
+ /* match dynamic devices first */
+ spin_lock(&p_drv->dynids.lock);
+ list_for_each_entry(dynid, &p_drv->dynids.list, node) {
+ ds_dev_dbg(3, dev, "trying to match to %s\n", drv->name);
+ if (pcmcia_devmatch(p_dev, &dynid->id)) {
+ ds_dev_dbg(0, dev, "matched to %s\n", drv->name);
+ spin_unlock(&p_drv->dynids.lock);
+ return 1;
+ }
+ }
+ spin_unlock(&p_drv->dynids.lock);
+#ifdef CONFIG_PCMCIA_IOCTL
/* matching by cardmgr */
- if (p_dev->cardmgr == p_drv)
+ if (p_dev->cardmgr == p_drv) {
+ ds_dev_dbg(0, dev, "cardmgr matched to %s\n", drv->name);
return 1;
+ }
+#endif
while (did && did->match_flags) {
- if (pcmcia_devmatch(p_dev, did))
+ ds_dev_dbg(3, dev, "trying to match to %s\n", drv->name);
+ if (pcmcia_devmatch(p_dev, did)) {
+ ds_dev_dbg(0, dev, "matched to %s\n", drv->name);
return 1;
+ }
did++;
}
#ifdef CONFIG_HOTPLUG
-static int pcmcia_bus_uevent(struct device *dev, char **envp, int num_envp,
- char *buffer, int buffer_size)
+static int pcmcia_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct pcmcia_device *p_dev;
- int i, length = 0;
+ int i;
u32 hash[4] = { 0, 0, 0, 0};
if (!dev)
hash[i] = crc32(0, p_dev->prod_id[i], strlen(p_dev->prod_id[i]));
}
- i = 0;
-
- if (add_uevent_var(envp, num_envp, &i,
- buffer, buffer_size, &length,
- "SOCKET_NO=%u",
- p_dev->socket->sock))
+ if (add_uevent_var(env, "SOCKET_NO=%u", p_dev->socket->sock))
return -ENOMEM;
- if (add_uevent_var(envp, num_envp, &i,
- buffer, buffer_size, &length,
- "DEVICE_NO=%02X",
- p_dev->device_no))
+ if (add_uevent_var(env, "DEVICE_NO=%02X", p_dev->device_no))
return -ENOMEM;
- if (add_uevent_var(envp, num_envp, &i,
- buffer, buffer_size, &length,
- "MODALIAS=pcmcia:m%04Xc%04Xf%02Xfn%02Xpfn%02X"
+ if (add_uevent_var(env, "MODALIAS=pcmcia:m%04Xc%04Xf%02Xfn%02Xpfn%02X"
"pa%08Xpb%08Xpc%08Xpd%08X",
p_dev->has_manf_id ? p_dev->manf_id : 0,
p_dev->has_card_id ? p_dev->card_id : 0,
hash[3]))
return -ENOMEM;
- envp[i] = NULL;
-
return 0;
}
#else
-static int pcmcia_bus_uevent(struct device *dev, char **envp, int num_envp,
- char *buffer, int buffer_size)
+static int pcmcia_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
{
return -ENODEV;
}
#endif
+/************************ runtime PM support ***************************/
+
+static int pcmcia_dev_suspend(struct device *dev, pm_message_t state);
+static int pcmcia_dev_resume(struct device *dev);
+
+static int runtime_suspend(struct device *dev)
+{
+ int rc;
+
+ down(&dev->sem);
+ rc = pcmcia_dev_suspend(dev, PMSG_SUSPEND);
+ up(&dev->sem);
+ return rc;
+}
+
+static void runtime_resume(struct device *dev)
+{
+ int rc;
+
+ down(&dev->sem);
+ rc = pcmcia_dev_resume(dev);
+ up(&dev->sem);
+}
+
/************************ per-device sysfs output ***************************/
#define pcmcia_device_attr(field, test, format) \
pcmcia_device_stringattr(prod_id3, prod_id[2]);
pcmcia_device_stringattr(prod_id4, prod_id[3]);
+
+static ssize_t pcmcia_show_pm_state(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
+
+ if (p_dev->suspended)
+ return sprintf(buf, "off\n");
+ else
+ return sprintf(buf, "on\n");
+}
+
+static ssize_t pcmcia_store_pm_state(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
+ int ret = 0;
+
+ if (!count)
+ return -EINVAL;
+
+ if ((!p_dev->suspended) && !strncmp(buf, "off", 3))
+ ret = runtime_suspend(dev);
+ else if (p_dev->suspended && !strncmp(buf, "on", 2))
+ runtime_resume(dev);
+
+ return ret ? ret : count;
+}
+
+
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
struct device_attribute *attr, const char *buf, size_t count)
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
- if (!count)
- return -EINVAL;
+ int ret;
+
+ if (!count)
+ return -EINVAL;
- down(&p_dev->socket->skt_sem);
+ mutex_lock(&p_dev->socket->skt_mutex);
p_dev->allow_func_id_match = 1;
- up(&p_dev->socket->skt_sem);
+ mutex_unlock(&p_dev->socket->skt_mutex);
- bus_rescan_devices(&pcmcia_bus_type);
+ ret = bus_rescan_devices(&pcmcia_bus_type);
+ if (ret)
+ printk(KERN_INFO "pcmcia: bus_rescan_devices failed after "
+ "allowing func_id matches\n");
return count;
}
static struct device_attribute pcmcia_dev_attrs[] = {
__ATTR(function, 0444, func_show, NULL),
+ __ATTR(pm_state, 0644, pcmcia_show_pm_state, pcmcia_store_pm_state),
__ATTR_RO(func_id),
__ATTR_RO(manf_id),
__ATTR_RO(card_id),
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
struct pcmcia_driver *p_drv = NULL;
+ int ret = 0;
+
+ if (p_dev->suspended)
+ return 0;
+
+ ds_dev_dbg(2, dev, "suspending\n");
if (dev->driver)
p_drv = to_pcmcia_drv(dev->driver);
- if (p_drv && p_drv->suspend)
- return p_drv->suspend(p_dev);
+ if (!p_drv)
+ goto out;
+
+ if (p_drv->suspend) {
+ ret = p_drv->suspend(p_dev);
+ if (ret) {
+ dev_printk(KERN_ERR, dev,
+ "pcmcia: device %s (driver %s) did "
+ "not want to go to sleep (%d)\n",
+ p_dev->devname, p_drv->drv.name, ret);
+ goto out;
+ }
+ }
+
+ if (p_dev->device_no == p_dev->func) {
+ ds_dev_dbg(2, dev, "releasing configuration\n");
+ pcmcia_release_configuration(p_dev);
+ }
- return 0;
+ out:
+ if (!ret)
+ p_dev->suspended = 1;
+ return ret;
}
{
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
struct pcmcia_driver *p_drv = NULL;
+ int ret = 0;
+
+ if (!p_dev->suspended)
+ return 0;
+
+ ds_dev_dbg(2, dev, "resuming\n");
if (dev->driver)
p_drv = to_pcmcia_drv(dev->driver);
- if (p_drv && p_drv->resume)
- return p_drv->resume(p_dev);
+ if (!p_drv)
+ goto out;
- return 0;
+ if (p_dev->device_no == p_dev->func) {
+ ds_dev_dbg(2, dev, "requesting configuration\n");
+ ret = pcmcia_request_configuration(p_dev, &p_dev->conf);
+ if (ret)
+ goto out;
+ }
+
+ if (p_drv->resume)
+ ret = p_drv->resume(p_dev);
+
+ out:
+ if (!ret)
+ p_dev->suspended = 0;
+ return ret;
}
struct pcmcia_socket *skt = _data;
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
- if (p_dev->socket != skt)
+ if (p_dev->socket != skt || p_dev->suspended)
return 0;
- return dpm_runtime_suspend(dev, PMSG_SUSPEND);
+ return runtime_suspend(dev);
}
static int pcmcia_bus_resume_callback(struct device *dev, void * _data)
struct pcmcia_socket *skt = _data;
struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
- if (p_dev->socket != skt)
+ if (p_dev->socket != skt || !p_dev->suspended)
return 0;
- dpm_runtime_resume(dev);
+ runtime_resume(dev);
return 0;
}
static int pcmcia_bus_resume(struct pcmcia_socket *skt)
{
+ ds_dev_dbg(2, &skt->dev, "resuming socket %d\n", skt->sock);
bus_for_each_dev(&pcmcia_bus_type, NULL, skt, pcmcia_bus_resume_callback);
return 0;
}
static int pcmcia_bus_suspend(struct pcmcia_socket *skt)
{
+ ds_dev_dbg(2, &skt->dev, "suspending socket %d\n", skt->sock);
if (bus_for_each_dev(&pcmcia_bus_type, NULL, skt,
pcmcia_bus_suspend_callback)) {
pcmcia_bus_resume(skt);
======================================================================*/
-struct send_event_data {
- struct pcmcia_socket *skt;
- event_t event;
- int priority;
-};
-
-static int send_event_callback(struct device *dev, void * _data)
-{
- struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
- struct pcmcia_driver *p_drv;
- struct send_event_data *data = _data;
-
- /* we get called for all sockets, but may only pass the event
- * for drivers _on the affected socket_ */
- if (p_dev->socket != data->skt)
- return 0;
-
- p_drv = to_pcmcia_drv(p_dev->dev.driver);
- if (!p_drv)
- return 0;
-
- if (p_dev->state & (CLIENT_UNBOUND|CLIENT_STALE))
- return 0;
-
- if (p_drv->event)
- return p_drv->event(data->event, data->priority,
- &p_dev->event_callback_args);
-
- return 0;
-}
-
-static int send_event(struct pcmcia_socket *s, event_t event, int priority)
-{
- struct send_event_data private;
-
- private.skt = s;
- private.event = event;
- private.priority = priority;
-
- return bus_for_each_dev(&pcmcia_bus_type, NULL, &private, send_event_callback);
-} /* send_event */
-
-
/* Normally, the event is passed to individual drivers after
* informing userspace. Only for CS_EVENT_CARD_REMOVAL this
* is inversed to maintain historic compatibility.
static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
{
struct pcmcia_socket *s = pcmcia_get_socket(skt);
- int ret = 0;
- ds_dbg(1, "ds_event(0x%06x, %d, 0x%p)\n",
- event, priority, skt);
-
- switch (event) {
+ if (!s) {
+ dev_printk(KERN_ERR, &skt->dev,
+ "PCMCIA obtaining reference to socket " \
+ "failed, event 0x%x lost!\n", event);
+ return -ENODEV;
+ }
+
+ ds_dev_dbg(1, &skt->dev, "ds_event(0x%06x, %d, 0x%p)\n",
+ event, priority, skt);
+ switch (event) {
case CS_EVENT_CARD_REMOVAL:
s->pcmcia_state.present = 0;
- send_event(skt, event, priority);
- pcmcia_card_remove(skt);
+ pcmcia_card_remove(skt, NULL);
handle_event(skt, event);
break;
-
+
case CS_EVENT_CARD_INSERTION:
s->pcmcia_state.present = 1;
pcmcia_card_add(skt);
break;
case CS_EVENT_EJECTION_REQUEST:
- ret = send_event(skt, event, priority);
break;
case CS_EVENT_PM_SUSPEND:
case CS_EVENT_PM_RESUME:
case CS_EVENT_RESET_PHYSICAL:
case CS_EVENT_CARD_RESET:
- handle_event(skt, event);
- break;
-
default:
handle_event(skt, event);
- send_event(skt, event, priority);
break;
}
} /* ds_event */
-
-int pcmcia_register_client(struct pcmcia_device **handle, client_reg_t *req)
+struct pcmcia_device * pcmcia_dev_present(struct pcmcia_device *_p_dev)
{
- struct pcmcia_socket *s = NULL;
- struct pcmcia_device *p_dev = NULL;
- struct pcmcia_driver *p_drv = NULL;
-
- /* Look for unbound client with matching dev_info */
- down_read(&pcmcia_socket_list_rwsem);
- list_for_each_entry(s, &pcmcia_socket_list, socket_list) {
- unsigned long flags;
-
- if (s->state & SOCKET_CARDBUS)
- continue;
+ struct pcmcia_device *p_dev;
+ struct pcmcia_device *ret = NULL;
- s = pcmcia_get_socket(s);
- if (!s)
- continue;
- spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
- list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
- p_dev = pcmcia_get_dev(p_dev);
- if (!p_dev)
- continue;
- if (!(p_dev->state & CLIENT_UNBOUND) ||
- (!p_dev->dev.driver)) {
- pcmcia_put_dev(p_dev);
- continue;
- }
- p_drv = to_pcmcia_drv(p_dev->dev.driver);
- if (!strncmp(p_drv->drv.name, (char *)req->dev_info, DEV_NAME_LEN)) {
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
- goto found;
- }
- pcmcia_put_dev(p_dev);
- }
- spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
- pcmcia_put_socket(s);
- }
- found:
- up_read(&pcmcia_socket_list_rwsem);
+ p_dev = pcmcia_get_dev(_p_dev);
if (!p_dev)
- return -ENODEV;
-
- pcmcia_put_socket(s); /* safe, as we already hold a reference from bind_device */
-
- *handle = p_dev;
- p_dev->state &= ~CLIENT_UNBOUND;
- p_dev->event_callback_args = req->event_callback_args;
- p_dev->event_callback_args.client_handle = p_dev;
-
-
- if (!s->functions) {
- cistpl_longlink_mfc_t mfc;
- if (pccard_read_tuple(s, p_dev->func, CISTPL_LONGLINK_MFC, &mfc)
- == CS_SUCCESS)
- s->functions = mfc.nfn;
- else
- s->functions = 1;
- s->config = kmalloc(sizeof(config_t) * s->functions,
- GFP_KERNEL);
- if (!s->config)
- goto out_no_resource;
- memset(s->config, 0, sizeof(config_t) * s->functions);
- }
+ return NULL;
- ds_dbg(1, "register_client(): client 0x%p, dev %s\n",
- p_dev, p_dev->dev.bus_id);
+ if (!p_dev->socket->pcmcia_state.present)
+ goto out;
- if ((s->state & (SOCKET_PRESENT|SOCKET_CARDBUS)) == SOCKET_PRESENT) {
- if (p_drv->event)
- p_drv->event(CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW,
- &p_dev->event_callback_args);
+ if (p_dev->_removed)
+ goto out;
- }
+ if (p_dev->suspended)
+ goto out;
- return CS_SUCCESS;
-
- out_no_resource:
+ ret = p_dev;
+ out:
pcmcia_put_dev(p_dev);
- return CS_OUT_OF_RESOURCE;
-} /* register_client */
-EXPORT_SYMBOL(pcmcia_register_client);
+ return ret;
+}
+EXPORT_SYMBOL(pcmcia_dev_present);
static struct pcmcia_callback pcmcia_bus_callback = {
.resume = pcmcia_bus_resume,
};
-static int __devinit pcmcia_bus_add_socket(struct class_device *class_dev,
+static int __devinit pcmcia_bus_add_socket(struct device *dev,
struct class_interface *class_intf)
{
- struct pcmcia_socket *socket = class_get_devdata(class_dev);
+ struct pcmcia_socket *socket = dev_get_drvdata(dev);
int ret;
socket = pcmcia_get_socket(socket);
if (!socket) {
- printk(KERN_ERR "PCMCIA obtaining reference to socket %p failed\n", socket);
+ dev_printk(KERN_ERR, dev,
+ "PCMCIA obtaining reference to socket failed\n");
return -ENODEV;
}
init_waitqueue_head(&socket->queue);
#endif
INIT_LIST_HEAD(&socket->devices_list);
- INIT_WORK(&socket->device_add, pcmcia_delayed_add_pseudo_device, socket);
+ INIT_WORK(&socket->device_add, pcmcia_delayed_add_device);
memset(&socket->pcmcia_state, 0, sizeof(u8));
socket->device_count = 0;
ret = pccard_register_pcmcia(socket, &pcmcia_bus_callback);
if (ret) {
- printk(KERN_ERR "PCMCIA registration PCCard core failed for socket %p\n", socket);
+ dev_printk(KERN_ERR, dev, "PCMCIA registration failed\n");
pcmcia_put_socket(socket);
return (ret);
}
return 0;
}
-static void pcmcia_bus_remove_socket(struct class_device *class_dev,
+static void pcmcia_bus_remove_socket(struct device *dev,
struct class_interface *class_intf)
{
- struct pcmcia_socket *socket = class_get_devdata(class_dev);
+ struct pcmcia_socket *socket = dev_get_drvdata(dev);
if (!socket)
return;
socket->pcmcia_state.dead = 1;
pccard_register_pcmcia(socket, NULL);
+ /* unregister any unbound devices */
+ mutex_lock(&socket->skt_mutex);
+ pcmcia_card_remove(socket, NULL);
+ mutex_unlock(&socket->skt_mutex);
+
pcmcia_put_socket(socket);
return;
/* the pcmcia_bus_interface is used to handle pcmcia socket devices */
-static struct class_interface pcmcia_bus_interface = {
+static struct class_interface pcmcia_bus_interface __refdata = {
.class = &pcmcia_socket_class,
- .add = &pcmcia_bus_add_socket,
- .remove = &pcmcia_bus_remove_socket,
+ .add_dev = &pcmcia_bus_add_socket,
+ .remove_dev = &pcmcia_bus_remove_socket,
};
.uevent = pcmcia_bus_uevent,
.match = pcmcia_bus_match,
.dev_attrs = pcmcia_dev_attrs,
+ .probe = pcmcia_device_probe,
+ .remove = pcmcia_device_remove,
.suspend = pcmcia_dev_suspend,
.resume = pcmcia_dev_resume,
};
static int __init init_pcmcia_bus(void)
{
+ int ret;
+
spin_lock_init(&pcmcia_dev_list_lock);
- bus_register(&pcmcia_bus_type);
- class_interface_register(&pcmcia_bus_interface);
+ ret = bus_register(&pcmcia_bus_type);
+ if (ret < 0) {
+ printk(KERN_WARNING "pcmcia: bus_register error: %d\n", ret);
+ return ret;
+ }
+ ret = class_interface_register(&pcmcia_bus_interface);
+ if (ret < 0) {
+ printk(KERN_WARNING
+ "pcmcia: class_interface_register error: %d\n", ret);
+ bus_unregister(&pcmcia_bus_type);
+ return ret;
+ }
pcmcia_setup_ioctl();