lkml.org 
[lkml]   [2003]   [Mar]   [25]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subject[PATCH] pcmcia: "driver services" socket add/remove abstraction
Previously, "Driver Services" could only be called when the socket
drivers were initialized earlier. This caused an awful lot of
problems, especially when modprobe tried to load ds.ko and a pcmcia
card driver at once.

As all socket devices are registered with the driver model core as
being of "class_type pcmcia_socket_class", we can take use of that and
register them with "Driver Services" upon detection or upon
module loading of ds.c.

Also, the "I-need-two-initcalls-in-a-module"-tweak can go away.

Unfortunately, this patch reportedly breaks some RedHat pcmcia init
scritps - they relied on the failed loading of ds.c to detect that no
socket driver was loaded previously. To properly detect this, you
should take a look at the /sys/class/pcmcia_socket/devices directory.

ds.c | 363 +++++++++++++++++++++++++++++++++++++++++--------------------------
1 files changed, 223 insertions(+), 140 deletions(-)

diff -ruN linux-original/drivers/pcmcia/ds.c linux/drivers/pcmcia/ds.c
--- linux-original/drivers/pcmcia/ds.c 2003-03-25 18:26:53.000000000 +0100
+++ linux/drivers/pcmcia/ds.c 2003-03-25 19:48:00.000000000 +0100
@@ -48,6 +48,7 @@
#include <linux/proc_fs.h>
#include <linux/poll.h>
#include <linux/pci.h>
+#include <linux/list.h>

#include <pcmcia/version.h>
#include <pcmcia/cs_types.h>
@@ -55,6 +56,7 @@
#include <pcmcia/bulkmem.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
+#include <pcmcia/ss.h>

/*====================================================================*/

@@ -105,6 +107,9 @@
wait_queue_head_t queue, request;
struct timer_list removal;
socket_bind_t *bind;
+ struct device *socket_dev;
+ struct list_head socket_list;
+ unsigned int socket_no; /* deprecated */
} socket_info_t;

#define SOCKET_PRESENT 0x01
@@ -116,8 +121,11 @@
/* Device driver ID passed to Card Services */
static dev_info_t dev_info = "Driver Services";

-static int sockets = 0, major_dev = -1;
-static socket_info_t *socket_table = NULL;
+static int major_dev = -1;
+
+/* list of all sockets registered with the pcmcia bus driver */
+static DECLARE_RWSEM(bus_socket_list_rwsem);
+static LIST_HEAD(bus_socket_list);

extern struct proc_dir_entry *proc_pccard;

@@ -135,6 +143,7 @@
/*======================================================================*/

static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info);
+static socket_info_t * get_socket_info_by_nr(unsigned int nr);

/**
* pcmcia_register_driver - register a PCMCIA driver with the bus core
@@ -160,17 +169,19 @@
void pcmcia_unregister_driver(struct pcmcia_driver *driver)
{
socket_bind_t *b;
- int i;
+ socket_info_t *bus_sock;

if (driver->use_count > 0) {
/* Blank out any left-over device instances */
driver->attach = NULL; driver->detach = NULL;
- for (i = 0; i < sockets; i++)
- for (b = socket_table[i].bind; b; b = b->next)
+ down_read(&bus_socket_list_rwsem);
+ list_for_each_entry(bus_sock, &bus_socket_list, socket_list) {
+ for (b = bus_sock->bind; b; b = b->next)
if (b->driver == driver)
b->instance = NULL;
- }
-
+ }
+ up_read(&bus_socket_list_rwsem);
+ }
driver_unregister(&driver->drv);
}
EXPORT_SYMBOL(pcmcia_unregister_driver);
@@ -182,7 +193,7 @@
{
struct pcmcia_driver *driver;
socket_bind_t *b;
- int i;
+ socket_info_t *bus_sock;

DEBUG(0, "ds: register_pccard_driver('%s')\n", (char *)dev_info);
driver = get_pcmcia_driver(dev_info);
@@ -199,15 +210,17 @@
if (driver->use_count == 0) return 0;

/* Instantiate any already-bound devices */
- for (i = 0; i < sockets; i++)
- for (b = socket_table[i].bind; b; b = b->next) {
+ down_read(&bus_socket_list_rwsem);
+ list_for_each_entry(bus_sock, &bus_socket_list, socket_list) {
+ for (b = bus_sock->bind; b; b = b->next) {
if (b->driver != driver) continue;
b->instance = driver->attach();
if (b->instance == NULL)
printk(KERN_NOTICE "ds: unable to create instance "
"of '%s'!\n", driver->drv.name);
}
-
+ }
+ up_read(&bus_socket_list_rwsem);
return 0;
} /* register_pccard_driver */

@@ -309,7 +322,7 @@

static void handle_removal(u_long sn)
{
- socket_info_t *s = &socket_table[sn];
+ socket_info_t *s = get_socket_info_by_nr(sn);
handle_event(s, CS_EVENT_CARD_REMOVAL);
s->state &= ~SOCKET_REMOVAL_PENDING;
}
@@ -329,7 +342,7 @@
DEBUG(1, "ds: ds_event(0x%06x, %d, 0x%p)\n",
event, priority, args->client_handle);
s = args->client_data;
- i = s - socket_table;
+ i = s->socket_no;

switch (event) {

@@ -400,9 +413,12 @@
struct pcmcia_driver *driver;
socket_bind_t *b;
bind_req_t bind_req;
- socket_info_t *s = &socket_table[i];
+ socket_info_t *s = get_socket_info_by_nr(i);
int ret;

+ if (!s)
+ return -EINVAL;
+
DEBUG(2, "bind_request(%d, '%s')\n", i,
(char *)bind_info->dev_info);
driver = get_pcmcia_driver(&bind_info->dev_info);
@@ -464,7 +480,7 @@

static int get_device_info(int i, bind_info_t *bind_info, int first)
{
- socket_info_t *s = &socket_table[i];
+ socket_info_t *s = get_socket_info_by_nr(i);
socket_bind_t *b;
dev_node_t *node;

@@ -534,7 +550,7 @@

static int unbind_request(int i, bind_info_t *bind_info)
{
- socket_info_t *s = &socket_table[i];
+ socket_info_t *s = get_socket_info_by_nr(i);
socket_bind_t **b, *c;

DEBUG(2, "unbind_request(%d, '%s')\n", i,
@@ -572,9 +588,11 @@
user_info_t *user;

DEBUG(0, "ds_open(socket %d)\n", i);
- if ((i >= sockets) || (sockets == 0))
- return -ENODEV;
- s = &socket_table[i];
+
+ s = get_socket_info_by_nr(i);
+ if (!s)
+ return -ENODEV;
+
if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
if (s->state & SOCKET_BUSY)
return -EBUSY;
@@ -604,9 +622,11 @@
user_info_t *user, **link;

DEBUG(0, "ds_release(socket %d)\n", i);
- if ((i >= sockets) || (sockets == 0))
- return 0;
- s = &socket_table[i];
+
+ s = get_socket_info_by_nr(i);
+ if (!s)
+ return 0;
+
user = file->private_data;
if (CHECK_USER(user))
goto out;
@@ -637,11 +657,13 @@

DEBUG(2, "ds_read(socket %d)\n", i);

- if ((i >= sockets) || (sockets == 0))
- return -ENODEV;
if (count < 4)
return -EINVAL;
- s = &socket_table[i];
+
+ s = get_socket_info_by_nr(i);
+ if (!s)
+ return -ENODEV;
+
user = file->private_data;
if (CHECK_USER(user))
return -EIO;
@@ -666,13 +688,15 @@

DEBUG(2, "ds_write(socket %d)\n", i);

- if ((i >= sockets) || (sockets == 0))
- return -ENODEV;
if (count != 4)
return -EINVAL;
if ((file->f_flags & O_ACCMODE) == O_RDONLY)
return -EBADF;
- s = &socket_table[i];
+
+ s = get_socket_info_by_nr(i);
+ if (!s)
+ return -ENODEV;
+
user = file->private_data;
if (CHECK_USER(user))
return -EIO;
@@ -699,9 +723,10 @@

DEBUG(2, "ds_poll(socket %d)\n", i);

- if ((i >= sockets) || (sockets == 0))
- return POLLERR;
- s = &socket_table[i];
+ s = get_socket_info_by_nr(i);
+ if (!s)
+ return POLLERR;
+
user = file->private_data;
if (CHECK_USER(user))
return POLLERR;
@@ -724,9 +749,9 @@

DEBUG(2, "ds_ioctl(socket %d, %#x, %#lx)\n", i, cmd, arg);

- if ((i >= sockets) || (sockets == 0))
- return -ENODEV;
- s = &socket_table[i];
+ s = get_socket_info_by_nr(i);
+ if (!s)
+ return -ENODEV;

size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL;
@@ -889,141 +914,199 @@

/*====================================================================*/

-struct bus_type pcmcia_bus_type = {
- .name = "pcmcia",
-};
-EXPORT_SYMBOL(pcmcia_bus_type);
-
-static int __init init_pcmcia_bus(void)
+static int __devinit pcmcia_bus_add_socket(struct device *dev, unsigned int socket_nr)
{
- bus_register(&pcmcia_bus_type);
- return 0;
-}
+ client_reg_t client_reg;
+ bind_req_t bind;
+ socket_info_t *s, *tmp_s;
+ int ret;
+ int i;

-static int __init init_pcmcia_ds(void)
-{
- client_reg_t client_reg;
- servinfo_t serv;
- bind_req_t bind;
- socket_info_t *s;
- int i, ret;
-
- DEBUG(0, "%s\n", version);
-
- /*
- * Ugly. But we want to wait for the socket threads to have started up.
- * We really should let the drivers themselves drive some of this..
- */
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(HZ/4);
+ s = kmalloc(sizeof(socket_info_t), GFP_KERNEL);
+ if(!s)
+ return -ENOMEM;
+ memset(s, 0, sizeof(socket_info_t));
+
+ /*
+ * Ugly. But we want to wait for the socket threads to have started up.
+ * We really should let the drivers themselves drive some of this..
+ */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/4);

- pcmcia_get_card_services_info(&serv);
- if (serv.Revision != CS_RELEASE_CODE) {
- printk(KERN_NOTICE "ds: Card Services release does not match!\n");
- return -1;
- }
- if (serv.Count == 0) {
- printk(KERN_NOTICE "ds: no socket drivers loaded!\n");
- return -1;
- }
-
- sockets = serv.Count;
- socket_table = kmalloc(sockets*sizeof(socket_info_t), GFP_KERNEL);
- if (!socket_table) return -1;
- for (i = 0, s = socket_table; i < sockets; i++, s++) {
- s->state = 0;
- s->user = NULL;
- s->req_pending = 0;
init_waitqueue_head(&s->queue);
init_waitqueue_head(&s->request);
- s->handle = NULL;
- init_timer(&s->removal);
- s->removal.data = i;
+
+ /* find the lowest, unused socket no. Please note that this is a
+ * temporary workaround until "struct pcmcia_socket" is introduced
+ * into cs.c which will include this number, and which will be
+ * accessible to ds.c directly */
+ i = 0;
+ next_try:
+ list_for_each_entry(tmp_s, &bus_socket_list, socket_list) {
+ if (tmp_s->socket_no == i) {
+ i++;
+ goto next_try;
+ }
+ }
+ s->socket_no = i;
+
+ /* initialize data */
+ s->socket_dev = dev;
+ s->removal.data = s->socket_no;
s->removal.function = &handle_removal;
- s->bind = NULL;
- }
+ init_timer(&s->removal);

- /* Set up hotline to Card Services */
- client_reg.dev_info = bind.dev_info = &dev_info;
- client_reg.Attributes = INFO_MASTER_CLIENT;
- client_reg.EventMask =
- CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
- CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
- CS_EVENT_EJECTION_REQUEST | CS_EVENT_INSERTION_REQUEST |
- CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
- client_reg.event_handler = &ds_event;
- client_reg.Version = 0x0210;
- for (i = 0; i < sockets; i++) {
- bind.Socket = i;
+ /* Set up hotline to Card Services */
+ client_reg.dev_info = bind.dev_info = &dev_info;
+
+ bind.Socket = s->socket_no;
bind.Function = BIND_FN_ALL;
ret = pcmcia_bind_device(&bind);
if (ret != CS_SUCCESS) {
- cs_error(NULL, BindDevice, ret);
- break;
+ cs_error(NULL, BindDevice, ret);
+ kfree(s);
+ return -EINVAL;
}
- client_reg.event_callback_args.client_data = &socket_table[i];
- ret = pcmcia_register_client(&socket_table[i].handle,
- &client_reg);
+
+ client_reg.Attributes = INFO_MASTER_CLIENT;
+ client_reg.EventMask =
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+ CS_EVENT_EJECTION_REQUEST | CS_EVENT_INSERTION_REQUEST |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.event_handler = &ds_event;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = s;
+ ret = pcmcia_register_client(&s->handle, &client_reg);
if (ret != CS_SUCCESS) {
- cs_error(NULL, RegisterClient, ret);
- break;
+ cs_error(NULL, RegisterClient, ret);
+ kfree(s);
+ return -EINVAL;
}
- }
-
- /* Set up character device for user mode clients */
- i = register_chrdev(0, "pcmcia", &ds_fops);
- if (i == -EBUSY)
- printk(KERN_NOTICE "unable to find a free device # for "
- "Driver Services\n");
- else
- major_dev = i;

-#ifdef CONFIG_PROC_FS
- if (proc_pccard)
- create_proc_read_entry("drivers",0,proc_pccard,proc_read_drivers,NULL);
- init_status = 0;
-#endif
- return 0;
+ list_add(&s->socket_list, &bus_socket_list);
+
+ return 0;
}


-static void __exit exit_pcmcia_ds(void)
+static int __devinit pcmcia_bus_add_socket_dev(struct device *dev)
{
- int i;
+ struct pcmcia_socket_class_data *cls_d = dev->class_data;
+ unsigned int i;
+ unsigned int ret = 0;
+
+ if (!cls_d)
+ return -ENODEV;
+
+ down_write(&bus_socket_list_rwsem);
+ for (i = 0; i < cls_d->nsock; i++)
+ ret += pcmcia_bus_add_socket(dev, i);
+ up_write(&bus_socket_list_rwsem);
+
+ return ret;
+}
+
+static int __devexit pcmcia_bus_remove_socket_dev(struct device *dev)
+{
+ struct pcmcia_socket_class_data *cls_d = dev->class_data;
+ struct list_head *list_loop;
+ struct list_head *tmp_storage;
+
+ if (!cls_d)
+ return -ENODEV;
+
+ down_write(&bus_socket_list_rwsem);
+ list_for_each_safe(list_loop, tmp_storage, &bus_socket_list) {
+ socket_info_t *bus_sock = container_of(list_loop, socket_info_t, socket_list);
+ if (bus_sock->socket_dev == dev) {
+ pcmcia_deregister_client(bus_sock->handle);
+ list_del(&bus_sock->socket_list);
+ kfree(bus_sock);
+ }
+ }
+ up_write(&bus_socket_list_rwsem);
+ return 0;
+}
+
+
+/* the pcmcia_bus_interface is used to handle pcmcia socket devices */
+static struct device_interface pcmcia_bus_interface = {
+ .name = "pcmcia-bus",
+ .devclass = &pcmcia_socket_class,
+ .add_device = &pcmcia_bus_add_socket_dev,
+ .remove_device = __devexit_p(&pcmcia_bus_remove_socket_dev),
+ .kset = { .subsys = &pcmcia_socket_class.subsys, },
+ .devnum = 0,
+};
+
+
+struct bus_type pcmcia_bus_type = {
+ .name = "pcmcia",
+};
+EXPORT_SYMBOL(pcmcia_bus_type);
+
+
+static int __init init_pcmcia_bus(void)
+{
+ int i;
+
+ bus_register(&pcmcia_bus_type);
+ interface_register(&pcmcia_bus_interface);
+
+ /* Set up character device for user mode clients */
+ i = register_chrdev(0, "pcmcia", &ds_fops);
+ if (i == -EBUSY)
+ printk(KERN_NOTICE "unable to find a free device # for "
+ "Driver Services\n");
+ else
+ major_dev = i;
+
#ifdef CONFIG_PROC_FS
- if (proc_pccard)
- remove_proc_entry("drivers", proc_pccard);
+ if (proc_pccard)
+ create_proc_read_entry("drivers",0,proc_pccard,proc_read_drivers,NULL);
#endif
- if (major_dev != -1)
- unregister_chrdev(major_dev, "pcmcia");
- for (i = 0; i < sockets; i++)
- pcmcia_deregister_client(socket_table[i].handle);
- sockets = 0;
- kfree(socket_table);
- bus_unregister(&pcmcia_bus_type);
+
+ return 0;
}
+fs_initcall(init_pcmcia_bus); /* one level after subsys_initcall so that
+ * pcmcia_socket_class is already registered */

-#ifdef MODULE

-/* init_pcmcia_bus must be done early, init_pcmcia_ds late. If we load this
- * as a module, we can only specify one initcall, though...
- */
-static int __init init_pcmcia_module(void) {
- init_pcmcia_bus();
- return init_pcmcia_ds();
-}
-module_init(init_pcmcia_module);
-
-#else /* !MODULE */
-subsys_initcall(init_pcmcia_bus);
-late_initcall(init_pcmcia_ds);
+static void __exit exit_pcmcia_bus(void)
+{
+ interface_unregister(&pcmcia_bus_interface);
+
+#ifdef CONFIG_PROC_FS
+ if (proc_pccard)
+ remove_proc_entry("drivers", proc_pccard);
#endif
+ if (major_dev != -1)
+ unregister_chrdev(major_dev, "pcmcia");
+
+ bus_unregister(&pcmcia_bus_type);
+}
+module_exit(exit_pcmcia_bus);

-module_exit(exit_pcmcia_ds);


/* helpers for backwards-compatible functions */

+
+static socket_info_t * get_socket_info_by_nr(unsigned int nr)
+{
+ socket_info_t * s;
+ down_read(&bus_socket_list_rwsem);
+ list_for_each_entry(s, &bus_socket_list, socket_list)
+ if (s->socket_no == nr) {
+ up_read(&bus_socket_list_rwsem);
+ return s;
+ }
+ up_read(&bus_socket_list_rwsem);
+ return NULL;
+}
+
/* backwards-compatible accessing of driver --- by name! */

struct cmp_data {
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
\
 
 \ /
  Last update: 2005-03-22 13:34    [W:0.044 / U:0.024 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site