Add a new registration function to register the PCMCIA 16-bit subsystem (ds a.k.a. pcmcia) with the PCMICA core (cs a.k.a. pcmcia_core). As send_event is only called with skt->sem held, we can use that to safeguard skt->callback(), too. Note that the class_device_register() call by pccardd() is done _before_ skt->sem() is held, and the pcmcia_socket_register() doesn't hold skt->sem() as well, so there is no chance for a deadlock. drivers/pcmcia/cs.c | 55 +++++++++++++++++++++++++++++++++++++++++-- drivers/pcmcia/cs_internal.h | 8 ++++++ drivers/pcmcia/ds.c | 51 +++++++++++---------------------------- include/pcmcia/ss.h | 2 + 4 files changed, 78 insertions(+), 38 deletions(-) diff -ruN linux-original/drivers/pcmcia/cs.c linux/drivers/pcmcia/cs.c --- linux-original/drivers/pcmcia/cs.c 2004-11-13 13:29:09.000000000 +0100 +++ linux/drivers/pcmcia/cs.c 2004-11-13 13:32:15.228152536 +0100 @@ -349,8 +349,6 @@ } } -static int send_event(struct pcmcia_socket *s, event_t event, int priority); - static void shutdown_socket(struct pcmcia_socket *s) { client_t **c; @@ -402,6 +400,26 @@ ======================================================================*/ + +static int pcmcia_send_event(struct pcmcia_socket *s, event_t event, int priority) +{ + int ret; + + if (!s->callback) + return 0; + if (!try_module_get(s->callback->owner)) + return 0; + + ret = s->callback->event(s, event, priority); + + module_put(s->callback->owner); + + return ret; +} + + +/* NOTE: send_event needs to be called with skt->sem held. */ + static int send_event(struct pcmcia_socket *s, event_t event, int priority) { client_t *client = s->clients; @@ -411,6 +429,11 @@ ret = 0; if (s->state & SOCKET_CARDBUS) return 0; + + ret = pcmcia_send_event(s, event, priority); + if (ret) + return (ret); + for (; client; client = client->next) { if (client->state & (CLIENT_UNBOUND|CLIENT_STALE)) continue; @@ -1363,6 +1386,34 @@ return CS_OUT_OF_RESOURCE; } /* register_client */ +/* register pcmcia_callback */ +int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) +{ + int ret = 0; + + /* s->skt_sem also protects s->callback */ + down(&s->skt_sem); + + if (c) { + /* registration */ + if (s->callback) { + ret = -EBUSY; + goto err; + } + + s->callback = c; + + if ((s->state & (SOCKET_PRESENT|SOCKET_CARDBUS)) == SOCKET_PRESENT) + pcmcia_send_event(s, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW); + } else + s->callback = NULL; + err: + up(&s->skt_sem); + + return ret; +} +EXPORT_SYMBOL(pccard_register_pcmcia); + /*====================================================================*/ int pcmcia_release_configuration(client_handle_t handle) diff -ruN linux-original/drivers/pcmcia/cs_internal.h linux/drivers/pcmcia/cs_internal.h --- linux-original/drivers/pcmcia/cs_internal.h 2004-11-13 13:29:09.000000000 +0100 +++ linux/drivers/pcmcia/cs_internal.h 2004-11-13 13:32:15.228152536 +0100 @@ -173,6 +173,14 @@ int pccard_get_status(struct pcmcia_socket *s, unsigned int function, cs_status_t *status); int pccard_access_configuration_register(struct pcmcia_socket *s, unsigned int function, conf_reg_t *reg); + +struct pcmcia_callback{ + struct module *owner; + int (*event) (struct pcmcia_socket *s, event_t event, int priority); +}; + +int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c); + #define cs_socket_name(skt) ((skt)->dev.class_id) #ifdef DEBUG diff -ruN linux-original/drivers/pcmcia/ds.c linux/drivers/pcmcia/ds.c --- linux-original/drivers/pcmcia/ds.c 2004-11-13 13:29:09.000000000 +0100 +++ linux/drivers/pcmcia/ds.c 2004-11-13 13:32:15.230152232 +0100 @@ -112,7 +112,7 @@ /* Socket state information */ struct pcmcia_bus_socket { atomic_t refcount; - client_handle_t handle; + struct pcmcia_callback callback; int state; user_info_t *user; int req_pending, req_result; @@ -486,14 +486,12 @@ ======================================================================*/ -static int ds_event(event_t event, int priority, - event_callback_args_t *args) +static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) { - struct pcmcia_bus_socket *s; + struct pcmcia_bus_socket *s = skt->pcmcia; ds_dbg(1, "ds_event(0x%06x, %d, 0x%p)\n", - event, priority, args->client_handle); - s = args->client_data; + event, priority, s); switch (event) { @@ -1084,8 +1082,6 @@ static int __devinit pcmcia_bus_add_socket(struct class_device *class_dev) { struct pcmcia_socket *socket = class_dev->class_data; - client_reg_t client_reg; - bind_req_t bind; struct pcmcia_bus_socket *s; int ret; @@ -1109,35 +1105,18 @@ s->parent = socket; /* Set up hotline to Card Services */ - client_reg.dev_info = bind.dev_info = &dev_info; - - bind.Socket = socket; - bind.Function = BIND_FN_ALL; - ret = pcmcia_bind_device(&bind); - if (ret != CS_SUCCESS) { - cs_error(NULL, BindDevice, ret); - kfree(s); - return -EINVAL; - } + s->callback.owner = THIS_MODULE; + s->callback.event = &ds_event; + socket->pcmcia = s; - 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); - kfree(s); - return -EINVAL; + ret = pccard_register_pcmcia(socket, &s->callback); + if (ret) { + printk(KERN_ERR "PCMCIA registration PCCard core failed for socket %p\n", socket); + pcmcia_put_bus_socket(s); + socket->pcmcia = NULL; + return (ret); } - socket->pcmcia = s; - return 0; } @@ -1149,9 +1128,9 @@ if (!socket || !socket->pcmcia) return; - flush_scheduled_work(); + pccard_register_pcmcia(socket, NULL); - pcmcia_deregister_client(socket->pcmcia->handle); + flush_scheduled_work(); socket->pcmcia->state |= DS_SOCKET_DEAD; pcmcia_put_bus_socket(socket->pcmcia); diff -ruN linux-original/include/pcmcia/ss.h linux/include/pcmcia/ss.h --- linux-original/include/pcmcia/ss.h 2004-11-13 13:29:39.000000000 +0100 +++ linux/include/pcmcia/ss.h 2004-11-13 13:32:15.230152232 +0100 @@ -159,6 +159,7 @@ struct config_t; struct region_t; +struct pcmcia_callback; struct pcmcia_socket { struct module *owner; @@ -215,6 +216,7 @@ /* pcmcia (16-bit) */ struct pcmcia_bus_socket *pcmcia; + struct pcmcia_callback *callback; /* cardbus (32-bit) */ #ifdef CONFIG_CARDBUS