From: Dominik Brodowski Add pcmcia_device(s). Signed-off-by: Dominik Brodowski Signed-off-by: Andrew Morton --- 25-akpm/drivers/pcmcia/ds.c | 73 ++++++++++++++++++++++++++++++++++++++++++++ 25-akpm/include/pcmcia/ds.h | 22 +++++++++++++ 2 files changed, 95 insertions(+) diff -puN drivers/pcmcia/ds.c~pcmcia-add-pcmcia_devices drivers/pcmcia/ds.c --- 25/drivers/pcmcia/ds.c~pcmcia-add-pcmcia_devices Mon Dec 13 14:38:37 2004 +++ 25-akpm/drivers/pcmcia/ds.c Mon Dec 13 14:38:37 2004 @@ -101,7 +101,15 @@ struct pcmcia_bus_socket { wait_queue_head_t queue, request; socket_bind_t *bind; struct pcmcia_socket *parent; + + /* the PCMCIA devices connected to this socket (normally one, more + * for multifunction devices: */ + struct list_head devices_list; + u8 device_count; /* the number of devices, used + * only internally and subject + * to incorrectness and change */ }; +static spinlock_t pcmcia_dev_list_lock; #define DS_SOCKET_PRESENT 0x01 #define DS_SOCKET_BUSY 0x02 @@ -328,6 +336,16 @@ static int proc_read_drivers(char *buf, } #endif +/* pcmcia_device handling */ + +static void pcmcia_release_dev(struct device *dev) +{ + struct pcmcia_device *p_dev = to_pcmcia_dev(dev); + p_dev->socket->pcmcia->device_count = 0; + kfree(p_dev); +} + + /*====================================================================== These manage a ring buffer of events pending for one user process @@ -491,8 +509,10 @@ static int bind_mtd(struct pcmcia_bus_so static int bind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info) { struct pcmcia_driver *driver; + struct pcmcia_device *p_dev; socket_bind_t *b; client_t *client; + unsigned long flags; if (!s) return -EINVAL; @@ -543,6 +563,38 @@ static int bind_request(struct pcmcia_bu b->next = s->bind; s->bind = b; + /* 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) { + /* FIXME: client isn't freed here */ + goto no_p_dev; + } + memset(p_dev, 0, sizeof(struct pcmcia_device)); + + p_dev->socket = s->parent; + p_dev->device_no = (s->device_count++); + 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->device_no); + p_dev->dev.driver = &driver->drv; + if (device_register(&p_dev->dev)) { + /* FIXME: client isn't freed here */ + kfree(p_dev); + goto no_p_dev; + } + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + list_add_tail(&p_dev->socket_device_list, &s->devices_list); + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + + no_p_dev: + driver->use_count++; if (driver->attach) { b->instance = driver->attach(); @@ -632,6 +684,8 @@ static int get_device_info(struct pcmcia static int unbind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info) { socket_bind_t **b, *c; + struct pcmcia_device *p_dev; + unsigned long flags; ds_dbg(2, "unbind_request(%d, '%s')\n", s->parent->sock, (char *)bind_info->dev_info); @@ -652,6 +706,22 @@ static int unbind_request(struct pcmcia_ module_put(c->driver->owner); *b = c->next; kfree(c); + + restart: + /* unregister the pcmcia_device */ + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + list_for_each_entry(p_dev, &s->devices_list, socket_device_list) { + if (p_dev->func == bind_info->function) { + list_del(&p_dev->socket_device_list); + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + + device_unregister(&p_dev->dev); + + /* multiple devices may be registered to this "function" */ + goto restart; + } + } + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); return 0; } /* unbind_request */ @@ -1034,6 +1104,7 @@ static int __devinit pcmcia_bus_add_sock init_waitqueue_head(&s->queue); init_waitqueue_head(&s->request); + INIT_LIST_HEAD(&s->devices_list); /* initialize data */ s->parent = socket; @@ -1090,6 +1161,8 @@ static int __init init_pcmcia_bus(void) { int i; + spin_lock_init(&pcmcia_dev_list_lock); + bus_register(&pcmcia_bus_type); class_interface_register(&pcmcia_bus_interface); diff -puN include/pcmcia/ds.h~pcmcia-add-pcmcia_devices include/pcmcia/ds.h --- 25/include/pcmcia/ds.h~pcmcia-add-pcmcia_devices Mon Dec 13 14:38:37 2004 +++ 25-akpm/include/pcmcia/ds.h Mon Dec 13 14:38:37 2004 @@ -127,6 +127,8 @@ typedef struct dev_link_t { ((l) && ((l->state & ~DEV_BUSY) == (DEV_CONFIG|DEV_PRESENT))) +struct pcmcia_socket; + extern struct bus_type pcmcia_bus_type; struct pcmcia_driver { @@ -141,6 +143,26 @@ struct pcmcia_driver { int pcmcia_register_driver(struct pcmcia_driver *driver); void pcmcia_unregister_driver(struct pcmcia_driver *driver); +struct pcmcia_device { + /* the socket and the device_no [for multifunction devices] + uniquely define a pcmcia_device */ + struct pcmcia_socket *socket; + + u8 device_no; + + /* the hardware "function" device; certain subdevices can + * share one hardware "function" device. */ + u8 func; + + struct list_head socket_device_list; + + struct device dev; +}; + +#define to_pcmcia_dev(n) container_of(n, struct pcmcia_device, dev) +#define to_pcmcia_drv(n) container_of(n, struct pcmcia_driver, drv) + + /* error reporting */ void cs_error(client_handle_t handle, int func, int ret); _