[PATCHES] updated pcmcia_device patches

Dominik Brodowski linux at brodo.de
Wed Oct 1 13:53:43 BST 2003


As Larry noted, the semantics of the spin_lock/spin_unlock calls were wrong.
Attached are updated patches for 2.6.0-test6. The
move_call_to_detach_to_generic_code patch is dropped for the moment. The
others should work just fine though.

	Dominik

change-ds-license
pcmcia_device-2
remove_socket_bind_t-2
remove_internal_driver_use_count

-------------- next part --------------
As previously discussed and ack'ed by David Hinds, change the 
license of ds.c ands.h to GPL only.

 drivers/pcmcia/ds.c |   33 ++++++---------------------------
 include/pcmcia/ds.h |   27 +++------------------------
 2 files changed, 9 insertions(+), 51 deletions(-)

diff -ruN linux-original/drivers/pcmcia/ds.c linux/drivers/pcmcia/ds.c
--- linux-original/drivers/pcmcia/ds.c	2003-09-29 22:42:34.228551424 +0200
+++ linux/drivers/pcmcia/ds.c	2003-09-29 22:48:06.989964000 +0200
@@ -3,32 +3,11 @@
     PC Card Driver Services
     
     ds.c 1.112 2001/10/13 00:08:28
-    
-    The contents of this file are subject to the Mozilla Public
-    License Version 1.1 (the "License"); you may not use this file
-    except in compliance with the License. You may obtain a copy of
-    the License at http://www.mozilla.org/MPL/
-
-    Software distributed under the License is distributed on an "AS
-    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
-    implied. See the License for the specific language governing
-    rights and limitations under the License.
-
-    The initial developer of the original code is David A. Hinds
-    <dahinds at users.sourceforge.net>.  Portions created by David A. Hinds
-    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
-
-    Alternatively, the contents of this file may be used under the
-    terms of the GNU General Public License version 2 (the "GPL"), in
-    which case the provisions of the GPL are applicable instead of the
-    above.  If you wish to allow the use of your version of this file
-    only under the terms of the GPL and not to allow others to use
-    your version of this file under the MPL, indicate your decision
-    by deleting the provisions above and replace them with the notice
-    and other provisions required by the GPL.  If you do not delete
-    the provisions above, a recipient may use your version of this
-    file under either the MPL or the GPL.
-    
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
+     
 ======================================================================*/
 
 #include <linux/config.h>
@@ -65,7 +44,7 @@
 
 MODULE_AUTHOR("David Hinds <dahinds at users.sourceforge.net>");
 MODULE_DESCRIPTION("PCMCIA Driver Services " CS_RELEASE);
-MODULE_LICENSE("Dual MPL/GPL");
+MODULE_LICENSE("GPL");
 
 #define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
 
diff -ruN linux-original/include/pcmcia/ds.h linux/include/pcmcia/ds.h
--- linux-original/include/pcmcia/ds.h	2003-09-29 22:42:42.768253192 +0200
+++ linux/include/pcmcia/ds.h	2003-09-29 22:47:25.016344960 +0200
@@ -1,30 +1,9 @@
 /*
  * ds.h 1.56 2000/06/12 21:55:40
  *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License
- * at http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and
- * limitations under the License. 
- *
- * The initial developer of the original code is David A. Hinds
- * <dahinds at users.sourceforge.net>.  Portions created by David A. Hinds
- * are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in which
- * case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use
- * your version of this file under the MPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the MPL or the GPL.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
  */
 
 #ifndef _LINUX_DS_H
-------------- next part --------------
Introduce a new struct pcmcia_device. So far it doesn't do anything 
useful besides showing up in the sysfs tree, but this will change soon.

The device registration is only done after the userspace carmgr 
detected a pcmcia card, found it in its database, and then tries to bind
a driver for this device. This can only properly be moved up if the PCMCIA
socket resource management is done within the kernel. And this still has
to wait for other changes...

 drivers/pcmcia/ds.c |   68 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 
 include/pcmcia/ds.h |   16 ++++++++++++
 2 files changed, 82 insertions(+), 2 deletions(-)

diff -ruN linux-original/drivers/pcmcia/ds.c linux/drivers/pcmcia/ds.c
--- linux-original/drivers/pcmcia/ds.c	2003-09-29 22:49:49.528375792 +0200
+++ linux/drivers/pcmcia/ds.c	2003-09-29 23:50:33.033478824 +0200
@@ -87,7 +87,12 @@
 	socket_bind_t		*bind;
 	struct device		*socket_dev;
 	struct pcmcia_socket	*parent;
+
+	/* the PCMCIA devices connected to this socket (normally one, more
+	 * for multifunction devices: */
+	struct list_head	devices_list;
 };
+static spinlock_t pcmcia_dev_list_lock = SPIN_LOCK_UNLOCKED;
 
 #define SOCKET_PRESENT		0x01
 #define SOCKET_BUSY		0x02
@@ -169,6 +174,15 @@
 }
 #endif
 
+/* pcmcia_device handling */
+
+static void pcmcia_release_dev(struct device *dev)
+{
+	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
+	kfree(p_dev);
+}
+
+
 /*======================================================================
 
     These manage a ring buffer of events pending for one user process
@@ -306,9 +320,11 @@
 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;
     bind_req_t bind_req;
     int ret;
+    unsigned long flags;
 
     if (!s)
 	    return -EINVAL;
@@ -357,7 +373,36 @@
     b->instance = NULL;
     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)
+	    goto no_p_dev;
+    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 = &driver->drv;
+    ret = device_register(&p_dev->dev);
+    if (ret) {
+	    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:
+
     if (driver->attach) {
 	b->instance = driver->attach();
 	if (b->instance == NULL) {
@@ -367,7 +412,7 @@
 	    return -ENODEV;
 	}
     }
-    
+
     return 0;
 } /* bind_request */
 
@@ -443,6 +488,8 @@
 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;
 
     DEBUG(2, "unbind_request(%d, '%s')\n", s->parent->sock,
 	  (char *)bind_info->dev_info);
@@ -463,6 +510,22 @@
     module_put(c->driver->owner);
     *b = c->next;
     kfree(c);
+
+    /* 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)
+		    goto found;
+    }
+    spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+    return 0;
+
+ found:
+    list_del(&p_dev->socket_device_list);
+    spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+
+    device_unregister(&p_dev->dev);
+   
     return 0;
 } /* unbind_request */
 
@@ -822,6 +885,7 @@
 
 	init_waitqueue_head(&s->queue);
 	init_waitqueue_head(&s->request);
+	INIT_LIST_HEAD(&s->devices_list);
 
 	/* initialize data */
 	s->socket_dev = socket->dev.dev;
diff -ruN linux-original/include/pcmcia/ds.h linux/include/pcmcia/ds.h
--- linux-original/include/pcmcia/ds.h	2003-09-29 22:49:49.000000000 +0200
+++ linux/include/pcmcia/ds.h	2003-09-29 23:27:12.000000000 +0200
@@ -137,5 +137,21 @@
 /* error reporting */
 void cs_error(client_handle_t handle, int func, int ret);
 
+struct pcmcia_socket;
+
+struct pcmcia_device {
+	/* the socket and the function no [for multifunction devices]
+	   uniquely define a pcmcia_device */
+	struct pcmcia_socket	*socket;
+	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)
+
 #endif /* __KERNEL__ */
 #endif /* _LINUX_DS_H */
-------------- next part --------------
Remove the internal driver use_count in ds.c, as the reference counting 
is done in the module core anyways, and that reference count is available 
for cardmgr's usage by a call to module_refcount. And if 
!CONFIG_MODULE_UNLOAD, rmmod is useless anyways, so avoid that call by
cardmgr at all.

 drivers/pcmcia/ds.c |   14 ++++++++------
 include/pcmcia/ds.h |    1 -
 2 files changed, 8 insertions(+), 7 deletions(-)

diff -ruN linux-original/drivers/pcmcia/ds.c linux/drivers/pcmcia/ds.c
--- linux-original/drivers/pcmcia/ds.c	2003-10-01 12:18:09.598079112 +0200
+++ linux/drivers/pcmcia/ds.c	2003-10-01 12:21:58.311309416 +0200
@@ -123,7 +123,6 @@
 	if (!driver)
 		return -EINVAL;
 
- 	driver->use_count = 0;
 	driver->drv.bus = &pcmcia_bus_type;
 
 	return driver_register(&driver->drv);
@@ -145,10 +144,15 @@
 static int proc_read_drivers_callback(struct device_driver *driver, void *d)
 {
 	char **p = d;
-	struct pcmcia_driver *p_dev = container_of(driver, 
+	struct pcmcia_driver *p_drv = container_of(driver, 
 						   struct pcmcia_driver, drv);
-
-	*p += sprintf(*p, "%-24.24s 1 %d\n", driver->name, p_dev->use_count);
+	*p += sprintf(*p, "%-24.24s 1 %d\n", p_drv->drv.name, 
+#ifdef CONFIG_MODULE_UNLOAD
+		      module_refcount(p_drv->owner)
+#else
+		      1
+#endif
+		);
 	d = (void *) p;
 
 	return 0;
@@ -386,7 +390,6 @@
 	spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
 
 	/* try to create an "instance"... */
-	p_drv->use_count++;
 	if (p_drv->attach) {
 		p_dev->instance = p_drv->attach();
 		if (!p_dev->instance) {
@@ -510,7 +513,6 @@
 	/* detach the "instance" */
 	p_drv = to_pcmcia_drv(p_dev->dev.driver);
 	if (p_drv) {
-		p_drv->use_count--;
 		if ((p_drv->detach) && (p_dev->instance))
 			p_drv->detach(p_dev->instance);
 	}
diff -ruN linux-original/include/pcmcia/ds.h linux/include/pcmcia/ds.h
--- linux-original/include/pcmcia/ds.h	2003-10-01 12:18:09.599078960 +0200
+++ linux/include/pcmcia/ds.h	2003-10-01 12:20:28.688934088 +0200
@@ -123,7 +123,6 @@
 extern struct bus_type pcmcia_bus_type;
 
 struct pcmcia_driver {
-	int			use_count;
 	dev_link_t		*(*attach)(void);
 	void			(*detach)(dev_link_t *);
 	struct module		*owner;
-------------- next part --------------
Remove struct socket_bind_t by moving "dev_link_t *instance" to struct
pcmcia_device, and transforming all users of socket_bind_t to use struct
pcmcia_device instead. Also, CodingStyle updates for 
bind_request and unbind_request.

 drivers/pcmcia/ds.c |  279 +++++++++++++++++++++++++---------------------------
 include/pcmcia/ds.h |    4
 2 files changed, 139 insertions(+), 144 deletions(-)

diff -ruN linux-original/drivers/pcmcia/ds.c linux/drivers/pcmcia/ds.c
--- linux-original/drivers/pcmcia/ds.c	2003-10-01 12:03:03.955757672 +0200
+++ linux/drivers/pcmcia/ds.c	2003-10-01 12:14:57.335307496 +0200
@@ -57,13 +57,6 @@
 
 /*====================================================================*/
 
-typedef struct socket_bind_t {
-    struct pcmcia_driver	*driver;
-    u_char		function;
-    dev_link_t		*instance;
-    struct socket_bind_t *next;
-} socket_bind_t;
-
 /* Device user information */
 #define MAX_EVENTS	32
 #define USER_MAGIC	0x7ea4
@@ -84,8 +77,7 @@
 	int			req_pending, req_result;
 	wait_queue_head_t	queue, request;
 	struct work_struct	removal;
-	socket_bind_t		*bind;
-	struct device		*socket_dev;
+
 	struct pcmcia_socket	*parent;
 
 	/* the PCMCIA devices connected to this socket (normally one, more
@@ -319,109 +311,114 @@
 
 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;
-    bind_req_t bind_req;
-    int ret;
-    unsigned long flags;
+	struct pcmcia_driver	*p_drv;
+	struct pcmcia_device	*p_dev;
+	bind_req_t		bind_req;
+	int			ret;
+	unsigned long		flags;
 
-    if (!s)
-	    return -EINVAL;
+	if (!s)
+		return -EINVAL;
 
-    DEBUG(2, "bind_request(%d, '%s')\n", s->parent->sock,
-	  (char *)bind_info->dev_info);
-    driver = get_pcmcia_driver(&bind_info->dev_info);
-    if (!driver)
-	    return -EINVAL;
-
-    for (b = s->bind; b; b = b->next)
-	if ((driver == b->driver) &&
-	    (bind_info->function == b->function))
-	    break;
-    if (b != NULL) {
-	bind_info->instance = b->instance;
-	return -EBUSY;
-    }
-
-    if (!try_module_get(driver->owner))
-	    return -EINVAL;
-
-    bind_req.Socket = s->parent;
-    bind_req.Function = bind_info->function;
-    bind_req.dev_info = (dev_info_t *) driver->drv.name;
-    ret = pcmcia_bind_device(&bind_req);
-    if (ret != CS_SUCCESS) {
-	cs_error(NULL, BindDevice, ret);
-	printk(KERN_NOTICE "ds: unable to bind '%s' to socket %d\n",
-	       (char *)dev_info, s->parent->sock);
-	module_put(driver->owner);
-	return -ENODEV;
-    }
+	DEBUG(2, "bind_request(%d, '%s')\n", s->parent->sock,
+	      (char *)bind_info->dev_info);
 
-    /* Add binding to list for this socket */
-    driver->use_count++;
-    b = kmalloc(sizeof(socket_bind_t), GFP_KERNEL);
-    if (!b) 
-    {
-    	driver->use_count--;
-	module_put(driver->owner);
-	return -ENOMEM;    
-    }
-    b->driver = driver;
-    b->function = bind_info->function;
-    b->instance = NULL;
-    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.
-     */
+	/* find the driver which shall be bound to this device */
+	p_drv = get_pcmcia_driver(&bind_info->dev_info);
+	if (!p_drv)
+		return -EINVAL;
+	if (!try_module_get(p_drv->owner))
+		return -EINVAL;
 
-    p_dev = kmalloc(sizeof(struct pcmcia_device), GFP_KERNEL);
-    if (!p_dev)
-	    goto no_p_dev;
-    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 = &driver->drv;
-    ret = device_register(&p_dev->dev);
-    if (ret) {
-	    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);
+	/* verify that no such device exists at the moment. */
+	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) {
+			spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+			bind_info->instance = p_dev->instance;
+			ret = -EBUSY;
+			goto err_put;
+		}
+	}
+	spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+
+	/* pcmcia-cs bind -- will be unified soon */
+	bind_req.Socket = s->parent;
+	bind_req.Function = bind_info->function;
+	bind_req.dev_info = (dev_info_t *) p_drv->drv.name;
+	ret = pcmcia_bind_device(&bind_req);
+	if (ret != CS_SUCCESS) {
+		cs_error(NULL, BindDevice, ret);
+		printk(KERN_NOTICE "ds: unable to bind '%s' to socket %d\n",
+		       (char *)dev_info, s->parent->sock);
+		ret = -ENODEV;
+		goto err_put;
+	}
 
- no_p_dev:
+	/* Currently, the userspace pcmcia cardmgr detects pcmcia devices. 
+	 * Here this information is translated into a kernel
+	 * struct pcmcia_device.
+	 */
 
-    if (driver->attach) {
-	b->instance = driver->attach();
-	if (b->instance == NULL) {
-	    printk(KERN_NOTICE "ds: unable to create instance "
-		   "of '%s'!\n", (char *)bind_info->dev_info);
-	    module_put(driver->owner);
-	    return -ENODEV;
+	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));
 
-    return 0;
+	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;
+	ret = device_register(&p_dev->dev);
+	if (ret) {
+		ret = -ENODEV;
+		kfree(p_dev);
+		goto err_put;
+	}
+	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);
+
+	/* try to create an "instance"... */
+	p_drv->use_count++;
+	if (p_drv->attach) {
+		p_dev->instance = p_drv->attach();
+		if (!p_dev->instance) {
+			printk(KERN_NOTICE "ds: unable to create instance "
+			       "of '%s'!\n", (char *)bind_info->dev_info);
+			ret = -ENODEV;
+			goto err_unreg;
+		}
+	}
+
+	return 0;
+
+
+ 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);
+
+	device_unregister(&p_dev->dev);
+
+ err_put:
+	module_put(p_drv->owner);
+	return (ret);
 } /* bind_request */
 
 /*====================================================================*/
 
 static int get_device_info(struct pcmcia_bus_socket *s, bind_info_t *bind_info, int first)
 {
-    socket_bind_t *b;
     dev_node_t *node;
+    struct pcmcia_device *p_dev;
+    unsigned long flags;
 
 #ifdef CONFIG_CARDBUS
     /*
@@ -459,19 +456,23 @@
     }
 #endif
 
-    for (b = s->bind; b; b = b->next)
-	if ((strcmp((char *)b->driver->drv.name,
-		    (char *)bind_info->dev_info) == 0) &&
-	    (b->function == bind_info->function))
-	    break;
-    if (b == NULL) return -ENODEV;
-    if ((b->instance == NULL) ||
-	(b->instance->state & DEV_CONFIG_PENDING))
+    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)
+		    goto found;
+    }
+    spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+    return -ENODEV;
+
+ found:
+    spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+    if ((p_dev->instance == NULL) ||
+	(p_dev->instance->state & DEV_CONFIG_PENDING))
 	return -EAGAIN;
     if (first)
-	node = b->instance->dev;
+	node = p_dev->instance->dev;
     else
-	for (node = b->instance->dev; node; node = node->next)
+	for (node = p_dev->instance->dev; node; node = node->next)
 	    if (node == bind_info->next) break;
     if (node == NULL) return -ENODEV;
 
@@ -487,46 +488,37 @@
 
 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;
-
-    DEBUG(2, "unbind_request(%d, '%s')\n", s->parent->sock,
-	  (char *)bind_info->dev_info);
-    for (b = &s->bind; *b; b = &(*b)->next)
-	if ((strcmp((char *)(*b)->driver->drv.name,
-		    (char *)bind_info->dev_info) == 0) &&
-	    ((*b)->function == bind_info->function))
-	    break;
-    if (*b == NULL)
+	struct pcmcia_device	*p_dev;
+	struct pcmcia_driver	*p_drv;
+	unsigned long		flags;
+
+	DEBUG(2, "unbind_request(%d, '%s')\n", s->parent->sock,
+	      (char *)bind_info->dev_info);
+
+	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)
+			goto found;
+	}
+	spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
 	return -ENODEV;
-    
-    c = *b;
-    c->driver->use_count--;
-    if (c->driver->detach) {
-	if (c->instance)
-	    c->driver->detach(c->instance);
-    }
-    module_put(c->driver->owner);
-    *b = c->next;
-    kfree(c);
-
-    /* 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)
-		    goto found;
-    }
-    spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
-    return 0;
 
  found:
-    list_del(&p_dev->socket_device_list);
-    spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+	list_del(&p_dev->socket_device_list);
+	spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
 
-    device_unregister(&p_dev->dev);
-   
-    return 0;
+	/* detach the "instance" */
+	p_drv = to_pcmcia_drv(p_dev->dev.driver);
+	if (p_drv) {
+		p_drv->use_count--;
+		if ((p_drv->detach) && (p_dev->instance))
+			p_drv->detach(p_dev->instance);
+	}
+	module_put(p_drv->owner);
+
+	device_unregister(&p_dev->dev);
+
+	return 0;
 } /* unbind_request */
 
 /*======================================================================
@@ -888,7 +880,6 @@
 	INIT_LIST_HEAD(&s->devices_list);
 
 	/* initialize data */
-	s->socket_dev = socket->dev.dev;
 	INIT_WORK(&s->removal, handle_removal, s);
 	s->parent = socket;
 
diff -ruN linux-original/include/pcmcia/ds.h linux/include/pcmcia/ds.h
--- linux-original/include/pcmcia/ds.h	2003-10-01 12:03:03.955757672 +0200
+++ linux/include/pcmcia/ds.h	2003-10-01 12:11:40.692201792 +0200
@@ -147,6 +147,10 @@
 
 	struct list_head	socket_device_list;
 
+	/* deprecated, a cleaned up version will be moved into this 
+	   struct soon */
+	dev_link_t		*instance;
+
 	struct device		dev;
 };
 


More information about the linux-pcmcia mailing list