From: Dominik Brodowski <linux@dominikbrodowski.de>

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.

Signed-off-by: Dominik Brodowski <linux@brodo.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/pcmcia/cs.c          |   55 +++++++++++++++++++++++++++++++++--
 25-akpm/drivers/pcmcia/cs_internal.h |    8 +++++
 25-akpm/drivers/pcmcia/ds.c          |   51 +++++++++-----------------------
 25-akpm/include/pcmcia/ss.h          |    2 +
 4 files changed, 78 insertions(+), 38 deletions(-)

diff -puN drivers/pcmcia/cs.c~pcmcia-new-ds-cs-interface drivers/pcmcia/cs.c
--- 25/drivers/pcmcia/cs.c~pcmcia-new-ds-cs-interface	Mon Dec 13 14:38:30 2004
+++ 25-akpm/drivers/pcmcia/cs.c	Mon Dec 13 14:38:30 2004
@@ -349,8 +349,6 @@ static void free_regions(memory_handle_t
     }
 }
 
-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 void shutdown_socket(struct pcmci
     
 ======================================================================*/
 
+
+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 @@ static int send_event(struct pcmcia_sock
     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 @@ int pcmcia_register_client(client_handle
     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 -puN drivers/pcmcia/cs_internal.h~pcmcia-new-ds-cs-interface drivers/pcmcia/cs_internal.h
--- 25/drivers/pcmcia/cs_internal.h~pcmcia-new-ds-cs-interface	Mon Dec 13 14:38:30 2004
+++ 25-akpm/drivers/pcmcia/cs_internal.h	Mon Dec 13 14:38:30 2004
@@ -173,6 +173,14 @@ int pccard_reset_card(struct pcmcia_sock
 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 -puN drivers/pcmcia/ds.c~pcmcia-new-ds-cs-interface drivers/pcmcia/ds.c
--- 25/drivers/pcmcia/ds.c~pcmcia-new-ds-cs-interface	Mon Dec 13 14:38:30 2004
+++ 25-akpm/drivers/pcmcia/ds.c	Mon Dec 13 14:38:30 2004
@@ -112,7 +112,7 @@ typedef struct user_info_t {
 /* 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;
@@ -484,14 +484,12 @@ static void handle_removal(void *data)
     
 ======================================================================*/
 
-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) {
 	
@@ -1082,8 +1080,6 @@ static struct file_operations ds_fops = 
 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;
 
@@ -1107,35 +1103,18 @@ static int __devinit pcmcia_bus_add_sock
 	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;
 }
 
@@ -1147,9 +1126,9 @@ static void pcmcia_bus_remove_socket(str
 	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 -puN include/pcmcia/ss.h~pcmcia-new-ds-cs-interface include/pcmcia/ss.h
--- 25/include/pcmcia/ss.h~pcmcia-new-ds-cs-interface	Mon Dec 13 14:38:30 2004
+++ 25-akpm/include/pcmcia/ss.h	Mon Dec 13 14:38:30 2004
@@ -159,6 +159,7 @@ typedef struct window_t {
 
 struct config_t;
 struct region_t;
+struct pcmcia_callback;
 
 struct pcmcia_socket {
 	struct module			*owner;
@@ -215,6 +216,7 @@ struct pcmcia_socket {
 
 	/* pcmcia (16-bit) */
 	struct pcmcia_bus_socket	*pcmcia;
+	struct pcmcia_callback		*callback;
 
 	/* cardbus (32-bit) */
 #ifdef CONFIG_CARDBUS
_