Re-structure the adding of PCMCIA devices and the binding of devices and drivers by cardmgr in bind_device: pcmcia_add_device() adds a new PCMCIA device for a socket and a device function, if it hasn't been done before. Signed-off-by: Dominik Brodowski --- drivers/pcmcia/ds.c | 198 +++++++++++++++++++++++++++++++++++----------------- include/pcmcia/ds.h | 3 2 files changed, 140 insertions(+), 61 deletions(-) Index: 2.6.10-rc3/drivers/pcmcia/ds.c =================================================================== --- 2.6.10-rc3.orig/drivers/pcmcia/ds.c 2004-12-06 14:14:53.888565208 +0100 +++ 2.6.10-rc3/drivers/pcmcia/ds.c 2004-12-06 14:17:15.583024376 +0100 @@ -355,7 +355,8 @@ static void pcmcia_put_dev(struct pcmcia_device *p_dev) { - put_device(&p_dev->dev); + if (p_dev) + put_device(&p_dev->dev); } static void pcmcia_release_dev(struct device *dev) @@ -425,6 +426,122 @@ } +/* + * pcmcia_find_dev -- obtain a reference to a struct pcmcia_device by the socket and the function + */ +static struct pcmcia_device * pcmcia_find_dev(struct pcmcia_bus_socket *s, unsigned int function) +{ + struct pcmcia_device *tmp_dev, *p_dev = NULL; + unsigned long flags; + + s = pcmcia_get_bus_socket(s); + if (!s) + return NULL; + + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list) { + if (tmp_dev->func == function) { + p_dev = pcmcia_get_dev(tmp_dev); + break; + } + } + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + + pcmcia_put_bus_socket(s); + + return (p_dev); +} + +/* device_add_lock is needed to avoid double registration by cardmgr and kernel. + * Serializes pcmcia_device_add; will most likely be removed in future. + * + * While it has the caveat that adding new PCMCIA devices inside(!) device_register() + * 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 int pcmcia_device_add(struct pcmcia_bus_socket *s, unsigned int function, struct pcmcia_driver *p_drv) +{ + struct pcmcia_device *p_dev, *tmp_dev; + unsigned long flags; + int ret = 0; + + s = pcmcia_get_bus_socket(s); + if (!s) + return -ENODEV; + + down(&device_add_lock); + + p_dev = pcmcia_find_dev(s, function); + if (p_dev) { + ret = -EBUSY; + goto err_put_dev; + } + + /* + * Allocate a struct pcmcia_device and fill it with life + */ + p_dev = kmalloc(sizeof(struct pcmcia_device), GFP_KERNEL); + if (!p_dev) { + ret = -ENOMEM; + goto err_put; + } + memset(p_dev, 0, sizeof(struct pcmcia_device)); + + p_dev->socket = s->parent; + p_dev->func = function; + + p_dev->dev.bus = &pcmcia_bus_type; + p_dev->dev.parent = s->parent->dev.dev; + p_dev->dev.release = pcmcia_release_dev; + sprintf (p_dev->dev.bus_id, "pcmcia%d.%d", p_dev->socket->sock, p_dev->func); + + /* temporary workaround */ + p_dev->dev.driver = &p_drv->drv; + + /* compat */ + p_dev->client.client_magic = CLIENT_MAGIC; + p_dev->client.Socket = s->parent; + p_dev->client.Function = function; + p_dev->client.state = CLIENT_UNBOUND; + + /* + * Add to the list in pcmcia_bus_socket. Also, assert that no + * such device exists at the moment. + */ + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list) { + WARN_ON(tmp_dev->func == function); + } + list_add_tail(&p_dev->socket_device_list, &s->devices_list); + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + + ret = device_register(&p_dev->dev); + if (ret) { + ret = -EIO; + + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + list_del(&p_dev->socket_device_list); + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + + kfree(p_dev); + + goto err_put; + } + + up(&device_add_lock); + + return 0; + + err_put_dev: + pcmcia_put_dev(p_dev); + err_put: + up(&device_add_lock); + pcmcia_put_bus_socket(s); + return (ret); +} + /*====================================================================== These manage a ring buffer of events pending for one user process @@ -594,8 +711,7 @@ static int bind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info) { struct pcmcia_driver *p_drv; - struct pcmcia_device *p_dev, *tmp_dev; - unsigned long flags; + struct pcmcia_device *p_dev; int ret = 0; s = pcmcia_get_bus_socket(s); @@ -616,72 +732,31 @@ goto err_put_driver; } - /* Currently, the userspace pcmcia cardmgr detects pcmcia devices. - * Here this information is translated into a kernel - * struct pcmcia_device. - */ - - p_dev = kmalloc(sizeof(struct pcmcia_device), GFP_KERNEL); - if (!p_dev) { - ret = -ENOMEM; + ret = pcmcia_device_add(s, bind_info->function, p_drv); + if ((ret) && (ret != -EBUSY)) goto err_put_module; - } - memset(p_dev, 0, sizeof(struct pcmcia_device)); - - p_dev->socket = s->parent; - p_dev->func = bind_info->function; - - p_dev->dev.bus = &pcmcia_bus_type; - p_dev->dev.parent = s->parent->dev.dev; - p_dev->dev.release = pcmcia_release_dev; - sprintf (p_dev->dev.bus_id, "pcmcia%d.%d", p_dev->socket->sock, p_dev->func); - p_dev->dev.driver = &p_drv->drv; - /* compat */ - p_dev->client.client_magic = CLIENT_MAGIC; - p_dev->client.Socket = s->parent; - p_dev->client.Function = bind_info->function; - p_dev->client.state = CLIENT_UNBOUND; - - ret = device_register(&p_dev->dev); - if (ret) { - kfree(p_dev); + p_dev = pcmcia_find_dev(s, bind_info->function); + if (!p_dev) { + ret = -EBUSY; goto err_put_module; } - /* - * Add to the list in pcmcia_bus_socket. Also, assert that no - * such device exists at the moment. - */ - - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); - list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list) { - if (tmp_dev->func == bind_info->function) { - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - bind_info->instance = tmp_dev->instance; - ret = -EBUSY; - goto err_unregister; - } + /* if there's already a device registered, and it was registered + * by userspace before, we need to return the "instance". Therefore, + * we need to set the cardmgr flag */ + if (p_dev->cardmgr) { + bind_info->instance = p_dev->instance; + ret = -EBUSY; + goto err_put_device; } - list_add_tail(&p_dev->socket_device_list, &s->devices_list); - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - ret = pcmcia_device_probe(&p_dev->dev); - if (ret) - goto err_unregister; - - module_put(p_drv->owner); - - put_driver(&p_drv->drv); - - return 0; - - err_unregister: - device_unregister(&p_dev->dev); - module_put(p_drv->owner); - put_driver(&p_drv->drv); - return (ret); + ret = pcmcia_device_probe(&p_dev->dev); + if (!ret) + p_dev->cardmgr++; + err_put_device: + pcmcia_put_dev(p_dev); err_put_module: module_put(p_drv->owner); err_put_driver: @@ -691,6 +766,7 @@ return (ret); } /* bind_request */ + int pcmcia_register_client(client_handle_t *handle, client_reg_t *req) { client_t *client = NULL; Index: 2.6.10-rc3/include/pcmcia/ds.h =================================================================== --- 2.6.10-rc3.orig/include/pcmcia/ds.h 2004-12-06 14:14:18.258981728 +0100 +++ 2.6.10-rc3/include/pcmcia/ds.h 2004-12-06 14:16:00.892379080 +0100 @@ -164,6 +164,9 @@ event_callback_args_t event_callback_args; } client; + /* registration by cardmgr done? */ + unsigned int cardmgr; + struct device dev; };