updated patches to handle multifunctional single-function devices
Dominik Brodowski
linux at dominikbrodowski.de
Mon Dec 13 11:05:03 EST 2004
I've uploaded new versions of some patches of the pcmcia-17, -18, -19, -20
and -21 patchsets to
http://kernel.org/pub/linux/kernel/people/brodo/patches/2.6.10-rc3/
http://kernel.org/pub/linux/kernel/people/brodo/patchsets/
and pushed the ones of -17, -18 and -19 to akpm.
The only functional change is in pcmcia-17-06-pcmcia-device, which I append
to this message: multiple drivers may be needed for one physical PCMCIA device
and for one physical PCMCIA "function". All other changes are just
"follow-ups" and/or rediffs to this functional change.
I'll update the additional patches already to be found at the places above
[2[2-4]-*] in the following days.
Dominik
Add pcmcia_device(s). They're named pcmciaX.Y with X being the socket number
and Y being the "device" number -- one PCMCIA card may have several
hardware-implemented functions (so-called multifunction devices) or may be
able to be driver by two drivers at the same time (e.g. pcnet_cs and
serial_cs).
Signed-off-by: Dominik Brodowski <linux at brodo.de>
Index: 2.6.10-rc3/drivers/pcmcia/ds.c
===================================================================
--- 2.6.10-rc3.orig/drivers/pcmcia/ds.c 2004-12-13 15:21:02.000000000 +0100
+++ 2.6.10-rc3/drivers/pcmcia/ds.c 2004-12-13 15:55:04.614965062 +0100
@@ -101,7 +101,15 @@
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 @@
}
#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_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 @@
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 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 @@
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 @@
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 @@
{
int i;
+ spin_lock_init(&pcmcia_dev_list_lock);
+
bus_register(&pcmcia_bus_type);
class_interface_register(&pcmcia_bus_interface);
Index: 2.6.10-rc3/include/pcmcia/ds.h
===================================================================
--- 2.6.10-rc3.orig/include/pcmcia/ds.h 2004-12-13 15:21:02.000000000 +0100
+++ 2.6.10-rc3/include/pcmcia/ds.h 2004-12-13 15:55:04.614965062 +0100
@@ -127,6 +127,8 @@
((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 @@
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);
More information about the linux-pcmcia
mailing list