Commit 1118d921 authored by Dominik Brodowski's avatar Dominik Brodowski Committed by Russell King

[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.
parent 811bbb96
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/list.h>
#include <pcmcia/version.h> #include <pcmcia/version.h>
#include <pcmcia/cs_types.h> #include <pcmcia/cs_types.h>
...@@ -55,6 +56,7 @@ ...@@ -55,6 +56,7 @@
#include <pcmcia/bulkmem.h> #include <pcmcia/bulkmem.h>
#include <pcmcia/cistpl.h> #include <pcmcia/cistpl.h>
#include <pcmcia/ds.h> #include <pcmcia/ds.h>
#include <pcmcia/ss.h>
/*====================================================================*/ /*====================================================================*/
...@@ -105,6 +107,9 @@ typedef struct socket_info_t { ...@@ -105,6 +107,9 @@ typedef struct socket_info_t {
wait_queue_head_t queue, request; wait_queue_head_t queue, request;
struct timer_list removal; struct timer_list removal;
socket_bind_t *bind; socket_bind_t *bind;
struct device *socket_dev;
struct list_head socket_list;
unsigned int socket_no; /* deprecated */
} socket_info_t; } socket_info_t;
#define SOCKET_PRESENT 0x01 #define SOCKET_PRESENT 0x01
...@@ -116,8 +121,11 @@ typedef struct socket_info_t { ...@@ -116,8 +121,11 @@ typedef struct socket_info_t {
/* Device driver ID passed to Card Services */ /* Device driver ID passed to Card Services */
static dev_info_t dev_info = "Driver Services"; static dev_info_t dev_info = "Driver Services";
static int sockets = 0, major_dev = -1; static int major_dev = -1;
static socket_info_t *socket_table = NULL;
/* 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; extern struct proc_dir_entry *proc_pccard;
...@@ -135,6 +143,7 @@ static void cs_error(client_handle_t handle, int func, int ret) ...@@ -135,6 +143,7 @@ static void cs_error(client_handle_t handle, int func, int ret)
/*======================================================================*/ /*======================================================================*/
static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info); 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 * pcmcia_register_driver - register a PCMCIA driver with the bus core
...@@ -160,17 +169,19 @@ EXPORT_SYMBOL(pcmcia_register_driver); ...@@ -160,17 +169,19 @@ EXPORT_SYMBOL(pcmcia_register_driver);
void pcmcia_unregister_driver(struct pcmcia_driver *driver) void pcmcia_unregister_driver(struct pcmcia_driver *driver)
{ {
socket_bind_t *b; socket_bind_t *b;
int i; socket_info_t *bus_sock;
if (driver->use_count > 0) { if (driver->use_count > 0) {
/* Blank out any left-over device instances */ /* Blank out any left-over device instances */
driver->attach = NULL; driver->detach = NULL; driver->attach = NULL; driver->detach = NULL;
for (i = 0; i < sockets; i++) down_read(&bus_socket_list_rwsem);
for (b = socket_table[i].bind; b; b = b->next) list_for_each_entry(bus_sock, &bus_socket_list, socket_list) {
for (b = bus_sock->bind; b; b = b->next)
if (b->driver == driver) if (b->driver == driver)
b->instance = NULL; b->instance = NULL;
} }
up_read(&bus_socket_list_rwsem);
}
driver_unregister(&driver->drv); driver_unregister(&driver->drv);
} }
EXPORT_SYMBOL(pcmcia_unregister_driver); EXPORT_SYMBOL(pcmcia_unregister_driver);
...@@ -182,7 +193,7 @@ int register_pccard_driver(dev_info_t *dev_info, ...@@ -182,7 +193,7 @@ int register_pccard_driver(dev_info_t *dev_info,
{ {
struct pcmcia_driver *driver; struct pcmcia_driver *driver;
socket_bind_t *b; socket_bind_t *b;
int i; socket_info_t *bus_sock;
DEBUG(0, "ds: register_pccard_driver('%s')\n", (char *)dev_info); DEBUG(0, "ds: register_pccard_driver('%s')\n", (char *)dev_info);
driver = get_pcmcia_driver(dev_info); driver = get_pcmcia_driver(dev_info);
...@@ -199,15 +210,17 @@ int register_pccard_driver(dev_info_t *dev_info, ...@@ -199,15 +210,17 @@ int register_pccard_driver(dev_info_t *dev_info,
if (driver->use_count == 0) return 0; if (driver->use_count == 0) return 0;
/* Instantiate any already-bound devices */ /* Instantiate any already-bound devices */
for (i = 0; i < sockets; i++) down_read(&bus_socket_list_rwsem);
for (b = socket_table[i].bind; b; b = b->next) { 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; if (b->driver != driver) continue;
b->instance = driver->attach(); b->instance = driver->attach();
if (b->instance == NULL) if (b->instance == NULL)
printk(KERN_NOTICE "ds: unable to create instance " printk(KERN_NOTICE "ds: unable to create instance "
"of '%s'!\n", driver->drv.name); "of '%s'!\n", driver->drv.name);
} }
}
up_read(&bus_socket_list_rwsem);
return 0; return 0;
} /* register_pccard_driver */ } /* register_pccard_driver */
...@@ -309,7 +322,7 @@ static int handle_request(socket_info_t *s, event_t event) ...@@ -309,7 +322,7 @@ static int handle_request(socket_info_t *s, event_t event)
static void handle_removal(u_long sn) 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); handle_event(s, CS_EVENT_CARD_REMOVAL);
s->state &= ~SOCKET_REMOVAL_PENDING; s->state &= ~SOCKET_REMOVAL_PENDING;
} }
...@@ -329,7 +342,7 @@ static int ds_event(event_t event, int priority, ...@@ -329,7 +342,7 @@ static int ds_event(event_t event, int priority,
DEBUG(1, "ds: ds_event(0x%06x, %d, 0x%p)\n", DEBUG(1, "ds: ds_event(0x%06x, %d, 0x%p)\n",
event, priority, args->client_handle); event, priority, args->client_handle);
s = args->client_data; s = args->client_data;
i = s - socket_table; i = s->socket_no;
switch (event) { switch (event) {
...@@ -400,9 +413,12 @@ static int bind_request(int i, bind_info_t *bind_info) ...@@ -400,9 +413,12 @@ static int bind_request(int i, bind_info_t *bind_info)
struct pcmcia_driver *driver; struct pcmcia_driver *driver;
socket_bind_t *b; socket_bind_t *b;
bind_req_t bind_req; bind_req_t bind_req;
socket_info_t *s = &socket_table[i]; socket_info_t *s = get_socket_info_by_nr(i);
int ret; int ret;
if (!s)
return -EINVAL;
DEBUG(2, "bind_request(%d, '%s')\n", i, DEBUG(2, "bind_request(%d, '%s')\n", i,
(char *)bind_info->dev_info); (char *)bind_info->dev_info);
driver = get_pcmcia_driver(&bind_info->dev_info); driver = get_pcmcia_driver(&bind_info->dev_info);
...@@ -464,7 +480,7 @@ static int bind_request(int i, bind_info_t *bind_info) ...@@ -464,7 +480,7 @@ static int bind_request(int i, bind_info_t *bind_info)
static int get_device_info(int i, bind_info_t *bind_info, int first) 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; socket_bind_t *b;
dev_node_t *node; dev_node_t *node;
...@@ -534,7 +550,7 @@ static int get_device_info(int i, bind_info_t *bind_info, int first) ...@@ -534,7 +550,7 @@ static int get_device_info(int i, bind_info_t *bind_info, int first)
static int unbind_request(int i, bind_info_t *bind_info) 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; socket_bind_t **b, *c;
DEBUG(2, "unbind_request(%d, '%s')\n", i, DEBUG(2, "unbind_request(%d, '%s')\n", i,
...@@ -572,9 +588,11 @@ static int ds_open(struct inode *inode, struct file *file) ...@@ -572,9 +588,11 @@ static int ds_open(struct inode *inode, struct file *file)
user_info_t *user; user_info_t *user;
DEBUG(0, "ds_open(socket %d)\n", i); DEBUG(0, "ds_open(socket %d)\n", i);
if ((i >= sockets) || (sockets == 0))
return -ENODEV; s = get_socket_info_by_nr(i);
s = &socket_table[i]; if (!s)
return -ENODEV;
if ((file->f_flags & O_ACCMODE) != O_RDONLY) { if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
if (s->state & SOCKET_BUSY) if (s->state & SOCKET_BUSY)
return -EBUSY; return -EBUSY;
...@@ -604,9 +622,11 @@ static int ds_release(struct inode *inode, struct file *file) ...@@ -604,9 +622,11 @@ static int ds_release(struct inode *inode, struct file *file)
user_info_t *user, **link; user_info_t *user, **link;
DEBUG(0, "ds_release(socket %d)\n", i); DEBUG(0, "ds_release(socket %d)\n", i);
if ((i >= sockets) || (sockets == 0))
return 0; s = get_socket_info_by_nr(i);
s = &socket_table[i]; if (!s)
return 0;
user = file->private_data; user = file->private_data;
if (CHECK_USER(user)) if (CHECK_USER(user))
goto out; goto out;
...@@ -637,11 +657,13 @@ static ssize_t ds_read(struct file *file, char *buf, ...@@ -637,11 +657,13 @@ static ssize_t ds_read(struct file *file, char *buf,
DEBUG(2, "ds_read(socket %d)\n", i); DEBUG(2, "ds_read(socket %d)\n", i);
if ((i >= sockets) || (sockets == 0))
return -ENODEV;
if (count < 4) if (count < 4)
return -EINVAL; return -EINVAL;
s = &socket_table[i];
s = get_socket_info_by_nr(i);
if (!s)
return -ENODEV;
user = file->private_data; user = file->private_data;
if (CHECK_USER(user)) if (CHECK_USER(user))
return -EIO; return -EIO;
...@@ -666,13 +688,15 @@ static ssize_t ds_write(struct file *file, const char *buf, ...@@ -666,13 +688,15 @@ static ssize_t ds_write(struct file *file, const char *buf,
DEBUG(2, "ds_write(socket %d)\n", i); DEBUG(2, "ds_write(socket %d)\n", i);
if ((i >= sockets) || (sockets == 0))
return -ENODEV;
if (count != 4) if (count != 4)
return -EINVAL; return -EINVAL;
if ((file->f_flags & O_ACCMODE) == O_RDONLY) if ((file->f_flags & O_ACCMODE) == O_RDONLY)
return -EBADF; return -EBADF;
s = &socket_table[i];
s = get_socket_info_by_nr(i);
if (!s)
return -ENODEV;
user = file->private_data; user = file->private_data;
if (CHECK_USER(user)) if (CHECK_USER(user))
return -EIO; return -EIO;
...@@ -699,9 +723,10 @@ static u_int ds_poll(struct file *file, poll_table *wait) ...@@ -699,9 +723,10 @@ static u_int ds_poll(struct file *file, poll_table *wait)
DEBUG(2, "ds_poll(socket %d)\n", i); DEBUG(2, "ds_poll(socket %d)\n", i);
if ((i >= sockets) || (sockets == 0)) s = get_socket_info_by_nr(i);
return POLLERR; if (!s)
s = &socket_table[i]; return POLLERR;
user = file->private_data; user = file->private_data;
if (CHECK_USER(user)) if (CHECK_USER(user))
return POLLERR; return POLLERR;
...@@ -724,9 +749,9 @@ static int ds_ioctl(struct inode * inode, struct file * file, ...@@ -724,9 +749,9 @@ static int ds_ioctl(struct inode * inode, struct file * file,
DEBUG(2, "ds_ioctl(socket %d, %#x, %#lx)\n", i, cmd, arg); DEBUG(2, "ds_ioctl(socket %d, %#x, %#lx)\n", i, cmd, arg);
if ((i >= sockets) || (sockets == 0)) s = get_socket_info_by_nr(i);
return -ENODEV; if (!s)
s = &socket_table[i]; return -ENODEV;
size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL; if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL;
...@@ -889,141 +914,199 @@ EXPORT_SYMBOL(unregister_pccard_driver); ...@@ -889,141 +914,199 @@ EXPORT_SYMBOL(unregister_pccard_driver);
/*====================================================================*/ /*====================================================================*/
struct bus_type pcmcia_bus_type = { static int __devinit pcmcia_bus_add_socket(struct device *dev, unsigned int socket_nr)
.name = "pcmcia",
};
EXPORT_SYMBOL(pcmcia_bus_type);
static int __init init_pcmcia_bus(void)
{ {
bus_register(&pcmcia_bus_type); client_reg_t client_reg;
return 0; bind_req_t bind;
} socket_info_t *s, *tmp_s;
int ret;
int i;
static int __init init_pcmcia_ds(void) s = kmalloc(sizeof(socket_info_t), GFP_KERNEL);
{ if(!s)
client_reg_t client_reg; return -ENOMEM;
servinfo_t serv; memset(s, 0, sizeof(socket_info_t));
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..
* 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);
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->queue);
init_waitqueue_head(&s->request); init_waitqueue_head(&s->request);
s->handle = NULL;
init_timer(&s->removal); /* find the lowest, unused socket no. Please note that this is a
s->removal.data = i; * 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->removal.function = &handle_removal;
s->bind = NULL; init_timer(&s->removal);
}
/* Set up hotline to Card Services */ /* Set up hotline to Card Services */
client_reg.dev_info = bind.dev_info = &dev_info; client_reg.dev_info = bind.dev_info = &dev_info;
client_reg.Attributes = INFO_MASTER_CLIENT;
client_reg.EventMask = bind.Socket = s->socket_no;
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;
bind.Function = BIND_FN_ALL; bind.Function = BIND_FN_ALL;
ret = pcmcia_bind_device(&bind); ret = pcmcia_bind_device(&bind);
if (ret != CS_SUCCESS) { if (ret != CS_SUCCESS) {
cs_error(NULL, BindDevice, ret); cs_error(NULL, BindDevice, ret);
break; kfree(s);
return -EINVAL;
} }
client_reg.event_callback_args.client_data = &socket_table[i];
ret = pcmcia_register_client(&socket_table[i].handle, client_reg.Attributes = INFO_MASTER_CLIENT;
&client_reg); 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) { if (ret != CS_SUCCESS) {
cs_error(NULL, RegisterClient, ret); cs_error(NULL, RegisterClient, ret);
break; 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 list_add(&s->socket_list, &bus_socket_list);
if (proc_pccard)
create_proc_read_entry("drivers",0,proc_pccard,proc_read_drivers,NULL); return 0;
init_status = 0;
#endif
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 #ifdef CONFIG_PROC_FS
if (proc_pccard) if (proc_pccard)
remove_proc_entry("drivers", proc_pccard); create_proc_read_entry("drivers",0,proc_pccard,proc_read_drivers,NULL);
#endif #endif
if (major_dev != -1)
unregister_chrdev(major_dev, "pcmcia"); return 0;
for (i = 0; i < sockets; i++)
pcmcia_deregister_client(socket_table[i].handle);
sockets = 0;
kfree(socket_table);
bus_unregister(&pcmcia_bus_type);
} }
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 static void __exit exit_pcmcia_bus(void)
* as a module, we can only specify one initcall, though... {
*/ interface_unregister(&pcmcia_bus_interface);
static int __init init_pcmcia_module(void) {
init_pcmcia_bus();
return init_pcmcia_ds();
}
module_init(init_pcmcia_module);
#else /* !MODULE */ #ifdef CONFIG_PROC_FS
subsys_initcall(init_pcmcia_bus); if (proc_pccard)
late_initcall(init_pcmcia_ds); remove_proc_entry("drivers", proc_pccard);
#endif #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 */ /* 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! */ /* backwards-compatible accessing of driver --- by name! */
struct cmp_data { struct cmp_data {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment