diff options
-rw-r--r-- | drivers/pcmcia/cs.c | 2 | ||||
-rw-r--r-- | drivers/pcmcia/cs_internal.h | 2 | ||||
-rw-r--r-- | drivers/pcmcia/ds.c | 70 | ||||
-rw-r--r-- | include/pcmcia/ss.h | 1 |
4 files changed, 45 insertions, 30 deletions
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 613f2f1fbfd..45cffbf285c 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -406,8 +406,6 @@ static void socket_shutdown(struct pcmcia_socket *s) cb_free(s); #endif s->functions = 0; - kfree(s->config); - s->config = NULL; s->ops->get_status(s, &status); if (status & SS_POWERON) { diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index 88c96aee0f4..01c32af559b 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -16,6 +16,7 @@ #define _LINUX_CS_INTERNAL_H #include <linux/config.h> +#include <linux/kref.h> /* Flags in client state */ #define CLIENT_CONFIG_LOCKED 0x0001 @@ -40,6 +41,7 @@ typedef struct region_t { /* Each card function gets one of these guys */ typedef struct config_t { + struct kref ref; u_int state; u_int Attributes; u_int IntType; diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 5166f00cfe3..37ba1246c28 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -23,6 +23,7 @@ #include <linux/workqueue.h> #include <linux/crc32.h> #include <linux/firmware.h> +#include <linux/kref.h> #define IN_CARD_SERVICES #include <pcmcia/cs_types.h> @@ -343,12 +344,19 @@ void pcmcia_put_dev(struct pcmcia_device *p_dev) put_device(&p_dev->dev); } +static void pcmcia_release_function(struct kref *ref) +{ + struct config_t *c = container_of(ref, struct config_t, ref); + 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); pcmcia_put_socket(p_dev->socket); kfree(p_dev->devname); + kref_put(&p_dev->function_config->ref, pcmcia_release_function); kfree(p_dev); } @@ -377,30 +385,14 @@ static int pcmcia_device_probe(struct device * dev) p_drv = to_pcmcia_drv(dev->driver); s = p_dev->socket; - if ((!p_drv->probe) || (!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; } p_dev->state &= ~CLIENT_UNBOUND; - /* set up the device configuration, if it hasn't been done before */ - 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 = kzalloc(sizeof(config_t) * s->functions, - GFP_KERNEL); - if (!s->config) { - ret = -ENOMEM; - goto put_module; - } - } - p_dev->function_config = &s->config[p_dev->func]; - ret = p_drv->probe(p_dev); if (ret) goto put_module; @@ -576,7 +568,7 @@ static DECLARE_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; @@ -597,6 +589,8 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f p_dev->socket = s; p_dev->device_no = (s->device_count++); p_dev->func = function; + if (s->functions < function) + s->functions = function; p_dev->dev.bus = &pcmcia_bus_type; p_dev->dev.parent = s->dev.dev; @@ -611,28 +605,50 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f /* compat */ p_dev->state = CLIENT_UNBOUND; - /* Add to the list in pcmcia_bus_socket */ + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + + /* + * 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; + kref_get(&p_dev->function_config->ref); + } + + /* Add to the list in pcmcia_bus_socket */ list_add_tail(&p_dev->socket_device_list, &s->devices_list); + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + if (!p_dev->function_config) { + 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); + } + printk(KERN_NOTICE "pcmcia: registering new device %s\n", p_dev->devname); pcmcia_device_query(p_dev); - 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); - - goto err_free; - } + if (device_register(&p_dev->dev)) + goto err_unreg; up(&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); diff --git a/include/pcmcia/ss.h b/include/pcmcia/ss.h index 2889a69a7a8..6529ccc7651 100644 --- a/include/pcmcia/ss.h +++ b/include/pcmcia/ss.h @@ -186,7 +186,6 @@ struct pcmcia_socket { u_short lock_count; pccard_mem_map cis_mem; void __iomem *cis_virt; - struct config_t *config; struct { u_int AssignedIRQ; u_int Config; |