linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [CFT] Fix PCMCIA deadlock (rev. 2)
@ 2003-04-29 14:17 Russell King
  2003-04-30 11:10 ` Felipe Alfaro Solana
  0 siblings, 1 reply; 2+ messages in thread
From: Russell King @ 2003-04-29 14:17 UTC (permalink / raw)
  To: Linux Kernel List

Ok, I think everyone ignored my last email about the updated pcmcia
deadlock patch.  I've done a little more work on it since then, so
here it is again.  Feedback welcome.

It should apply to bk-curr or 2.5.68 (probably with some offsets
though.)

I've tried all manner of cardctl operations vs plugging and unplugging
cards, as well as module insertion and removal - it seems pretty rock
solid for me.

diff -u orig/drivers/pcmcia/cs.c linux/drivers/pcmcia/cs.c
--- orig/drivers/pcmcia/cs.c	Sat Apr 26 23:00:59 2003
+++ linux/drivers/pcmcia/cs.c	Sat Apr 26 23:07:24 2003
@@ -302,11 +302,8 @@
     
 ======================================================================*/
 
-static int setup_socket(socket_info_t *);
-static void shutdown_socket(socket_info_t *);
-static void reset_socket(socket_info_t *);
-static void unreset_socket(socket_info_t *);
-static void parse_events(void *info, u_int events);
+static int pccardd(void *__skt);
+void pcmcia_unregister_socket(struct device *dev);
 
 #define to_class_data(dev) dev->class_data
 
@@ -317,7 +314,7 @@
 {
 	struct pcmcia_socket_class_data *cls_d = to_class_data(dev);
 	socket_info_t *s_info;
-	unsigned int i, j;
+	unsigned int i, j, ret;
 
 	if (!cls_d)
 		return -EINVAL;
@@ -330,6 +327,7 @@
 	memset(s_info, 0, cls_d->nsock * sizeof(socket_info_t));
 
 	cls_d->s_info = s_info;
+	ret = 0;
 
 	/* socket initialization */
 	for (i = 0; i < cls_d->nsock; i++) {
@@ -344,7 +342,7 @@
 		s->erase_busy.next = s->erase_busy.prev = &s->erase_busy;
 		INIT_LIST_HEAD(&s->cis_cache);
 		spin_lock_init(&s->lock);
-    
+
 		/* TBD: remove usage of socket_table, use class_for_each_dev instead */
 		for (j = 0; j < sockets; j++)
 			if (socket_table[j] == NULL) break;
@@ -353,6 +351,20 @@
 
 		init_socket(s);
 		s->ss_entry->inquire_socket(s->sock, &s->cap);
+
+		init_completion(&s->thread_done);
+		init_waitqueue_head(&s->thread_wait);
+		init_MUTEX(&s->skt_sem);
+		spin_lock_init(&s->thread_lock);
+		ret = kernel_thread(pccardd, s, CLONE_KERNEL);
+		if (ret < 0) {
+			pcmcia_unregister_socket(dev);
+			break;
+		}
+
+		wait_for_completion(&s->thread_done);
+		BUG_ON(!s->thread);
+
 #ifdef CONFIG_PROC_FS
 		if (proc_pccard) {
 			char name[3];
@@ -368,7 +380,7 @@
 		}
 #endif
 	}
-	return 0;
+	return ret;
 } /* pcmcia_register_socket */
 
 
@@ -407,8 +419,12 @@
 			remove_proc_entry(name, proc_pccard);
 		}
 #endif
-		
-		shutdown_socket(s);
+		if (s->thread) {
+			init_completion(&s->thread_done);
+			s->thread = NULL;
+			wake_up(&s->thread_wait);
+			wait_for_completion(&s->thread_done);
+		}
 		release_cis_mem(s);
 		while (s->clients) {
 			client = s->clients;
@@ -450,15 +466,6 @@
 
 static int send_event(socket_info_t *s, event_t event, int priority);
 
-/*
- * Sleep for n_cs centiseconds (1 cs = 1/100th of a second)
- */
-static void cs_sleep(unsigned int n_cs)
-{
-	current->state = TASK_INTERRUPTIBLE;
-	schedule_timeout( (n_cs * HZ + 99) / 100);
-}
-
 static void shutdown_socket(socket_info_t *s)
 {
     client_t **c;
@@ -505,132 +512,6 @@
     free_regions(&s->c_region);
 } /* shutdown_socket */
 
-/*
- * Return zero if we think the card isn't actually present
- */
-static int setup_socket(socket_info_t *s)
-{
-	int val, ret;
-	int setup_timeout = 100;
-
-	/* Wait for "not pending" */
-	for (;;) {
-		get_socket_status(s, &val);
-		if (!(val & SS_PENDING))
-			break;
-		if (--setup_timeout) {
-			cs_sleep(10);
-			continue;
-		}
-		printk(KERN_NOTICE "cs: socket %p voltage interrogation"
-			" timed out\n", s);
-		ret = 0;
-		goto out;
-	}
-
-	if (val & SS_DETECT) {
-		DEBUG(1, "cs: setup_socket(%p): applying power\n", s);
-		s->state |= SOCKET_PRESENT;
-		s->socket.flags &= SS_DEBOUNCED;
-		if (val & SS_3VCARD)
-		    s->socket.Vcc = s->socket.Vpp = 33;
-		else if (!(val & SS_XVCARD))
-		    s->socket.Vcc = s->socket.Vpp = 50;
-		else {
-		    printk(KERN_NOTICE "cs: socket %p: unsupported "
-			   "voltage key\n", s);
-		    s->socket.Vcc = 0;
-		}
-		if (val & SS_CARDBUS) {
-		    s->state |= SOCKET_CARDBUS;
-#ifndef CONFIG_CARDBUS
-		    printk(KERN_NOTICE "cs: unsupported card type detected!\n");
-#endif
-		}
-		set_socket(s, &s->socket);
-		cs_sleep(vcc_settle);
-		reset_socket(s);
-		ret = 1;
-	} else {
-		DEBUG(0, "cs: setup_socket(%p): no card!\n", s);
-		ret = 0;
-	}
-out:
-	return ret;
-} /* setup_socket */
-
-/*======================================================================
-
-    Reset_socket() and unreset_socket() handle hard resets.  Resets
-    have several causes: card insertion, a call to reset_socket, or
-    recovery from a suspend/resume cycle.  Unreset_socket() sends
-    a CS event that matches the cause of the reset.
-    
-======================================================================*/
-
-static void reset_socket(socket_info_t *s)
-{
-    DEBUG(1, "cs: resetting socket %p\n", s);
-    s->socket.flags |= SS_OUTPUT_ENA | SS_RESET;
-    set_socket(s, &s->socket);
-    udelay((long)reset_time);
-    s->socket.flags &= ~SS_RESET;
-    set_socket(s, &s->socket);
-    cs_sleep(unreset_delay);
-    unreset_socket(s);
-} /* reset_socket */
-
-#define EVENT_MASK \
-(SOCKET_SETUP_PENDING|SOCKET_SUSPEND|SOCKET_RESET_PENDING)
-
-static void unreset_socket(socket_info_t *s)
-{
-	int setup_timeout = unreset_limit;
-	int val;
-
-	/* Wait for "ready" */
-	for (;;) {
-		get_socket_status(s, &val);
-		if (val & SS_READY)
-			break;
-		DEBUG(2, "cs: socket %d not ready yet\n", s->sock);
-		if (--setup_timeout) {
-			cs_sleep(unreset_check);
-			continue;
-		}
-		printk(KERN_NOTICE "cs: socket %p timed out during"
-			" reset.  Try increasing setup_delay.\n", s);
-		s->state &= ~EVENT_MASK;
-		return;
-	}
-
-	DEBUG(1, "cs: reset done on socket %p\n", s);
-	if (s->state & SOCKET_SUSPEND) {
-	    s->state &= ~EVENT_MASK;
-	    if (verify_cis_cache(s) != 0)
-		parse_events(s, SS_DETECT);
-	    else
-		send_event(s, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
-	} else if (s->state & SOCKET_SETUP_PENDING) {
-#ifdef CONFIG_CARDBUS
-	    if (s->state & SOCKET_CARDBUS) {
-		cb_alloc(s);
-		s->state |= SOCKET_CARDBUS_CONFIG;
-	    }
-#endif
-	    send_event(s, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
-	    s->state &= ~SOCKET_SETUP_PENDING;
-	} else {
-	    send_event(s, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW);
-	    if (s->reset_handle) { 
-		    s->reset_handle->event_callback_args.info = NULL;
-		    EVENT(s->reset_handle, CS_EVENT_RESET_COMPLETE,
-			  CS_EVENT_PRI_LOW);
-	    }
-	    s->state &= ~EVENT_MASK;
-	}
-} /* unreset_socket */
-
 /*======================================================================
 
     The central event handler.  Send_event() sends an event to all
@@ -661,61 +542,266 @@
     return ret;
 } /* send_event */
 
-static void do_shutdown(socket_info_t *s)
+static void pcmcia_error(socket_info_t *skt, const char *fmt, ...)
 {
-    client_t *client;
-    if (s->state & SOCKET_SHUTDOWN_PENDING)
-	return;
-    s->state |= SOCKET_SHUTDOWN_PENDING;
-    send_event(s, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
-    for (client = s->clients; client; client = client->next)
-	if (!(client->Attributes & INFO_MASTER_CLIENT))
-	    client->state |= CLIENT_STALE;
-    if (s->state & (SOCKET_SETUP_PENDING|SOCKET_RESET_PENDING)) {
-	DEBUG(0, "cs: flushing pending setup\n");
-	s->state &= ~EVENT_MASK;
-    }
-    cs_sleep(shutdown_delay);
-    s->state &= ~SOCKET_PRESENT;
-    shutdown_socket(s);
+	static char buf[128];
+	va_list ap;
+	int len;
+
+	va_start(ap, fmt);
+	len = vsnprintf(buf, sizeof(buf), fmt, ap);
+	va_end(ap);
+	buf[len] = '\0';
+
+	printk(KERN_ERR "PCMCIA: socket %p: %s", skt, buf);
+}
+
+#define cs_to_timeout(cs) (((cs) * HZ + 99) / 100)
+
+static void socket_remove_drivers(socket_info_t *skt)
+{
+	client_t *client;
+
+	send_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
+
+	for (client = skt->clients; client; client = client->next)
+		if (!(client->Attributes & INFO_MASTER_CLIENT))
+			client->state |= CLIENT_STALE;
+}
+
+static void socket_shutdown(socket_info_t *skt)
+{
+	socket_remove_drivers(skt);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(cs_to_timeout(shutdown_delay));
+	skt->state &= ~SOCKET_PRESENT;
+	shutdown_socket(skt);
+}
+
+static int socket_reset(socket_info_t *skt)
+{
+	int status, i;
+
+	skt->socket.flags |= SS_OUTPUT_ENA | SS_RESET;
+	set_socket(skt, &skt->socket);
+	udelay((long)reset_time);
+
+	skt->socket.flags &= ~SS_RESET;
+	set_socket(skt, &skt->socket);
+
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(cs_to_timeout(unreset_delay));
+	for (i = 0; i < unreset_limit; i++) {
+		get_socket_status(skt, &status);
+
+		if (!(status & SS_DETECT))
+			return CS_NO_CARD;
+
+		if (status & SS_READY)
+			return CS_SUCCESS;
+
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(cs_to_timeout(unreset_check));
+	}
+
+	pcmcia_error(skt, "time out after reset.\n");
+	return CS_GENERAL_FAILURE;
+}
+
+static int socket_setup(socket_info_t *skt, int initial_delay)
+{
+	int status, i;
+
+	get_socket_status(skt, &status);
+	if (!(status & SS_DETECT))
+		return CS_NO_CARD;
+
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(cs_to_timeout(initial_delay));
+
+	for (i = 0; i < 100; i++) {
+		get_socket_status(skt, &status);
+		if (!(status & SS_DETECT))
+			return CS_NO_CARD;
+
+		if (!(status & SS_PENDING))
+			break;
+
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(cs_to_timeout(10));
+	}
+
+	if (status & SS_PENDING) {
+		pcmcia_error(skt, "voltage interrogation timed out.\n");
+		return CS_GENERAL_FAILURE;
+	}
+
+	if (status & SS_CARDBUS) {
+		skt->state |= SOCKET_CARDBUS;
+#ifndef CONFIG_CARDBUS
+		pcmcia_error(skt, "cardbus cards are not supported.\n");
+		return CS_BAD_TYPE;
+#endif
+	}
+
+	/*
+	 * Decode the card voltage requirements, and apply power to the card.
+	 */
+	if (status & SS_3VCARD)
+		skt->socket.Vcc = skt->socket.Vpp = 33;
+	else if (!(status & SS_XVCARD))
+		skt->socket.Vcc = skt->socket.Vpp = 50;
+	else {
+		pcmcia_error(skt, "unsupported voltage key.\n");
+		return CS_BAD_TYPE;
+	}
+	skt->state |= SOCKET_PRESENT;
+	skt->socket.flags = SS_DEBOUNCED;
+	set_socket(skt, &skt->socket);
+
+	/*
+	 * Wait "vcc_settle" for the supply to stabilise.
+	 */
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(cs_to_timeout(vcc_settle));
+
+	return socket_reset(skt);
+}
+
+/*
+ * Handle card insertion.  Setup the socket, reset the card,
+ * and then tell the rest of PCMCIA that a card is present.
+ */
+static int socket_insert(socket_info_t *skt)
+{
+	int ret;
+
+	ret = socket_setup(skt, setup_delay);
+	if (ret == CS_SUCCESS) {
+#ifdef CONFIG_CARDBUS
+		if (skt->state & SOCKET_CARDBUS) {
+			cb_alloc(skt);
+			skt->state |= SOCKET_CARDBUS_CONFIG;
+		}
+#endif
+		send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
+		skt->socket.flags &= ~SS_DEBOUNCED;
+	} else
+		socket_shutdown(skt);
+
+	return ret;
+}
+
+static int socket_suspend(socket_info_t *skt)
+{
+	if (skt->state & SOCKET_SUSPEND)
+		return CS_IN_USE;
+
+	send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
+	suspend_socket(skt);
+	skt->state |= SOCKET_SUSPEND;
+
+	return CS_SUCCESS;
+}
+
+/*
+ * Resume a socket.  If a card is present, verify its CIS against
+ * our cached copy.  If they are different, the card has been
+ * replaced, and we need to tell the drivers.
+ */
+static int socket_resume(socket_info_t *skt)
+{
+	int ret;
+
+	if (!(skt->state & SOCKET_SUSPEND))
+		return CS_IN_USE;
+
+	init_socket(skt);
+
+	ret = socket_setup(skt, resume_delay);
+	if (ret == CS_SUCCESS) {
+		/*
+		 * FIXME: need a better check here for cardbus cards.
+		 */
+		if (verify_cis_cache(skt) != 0) {
+			socket_remove_drivers(skt);
+			destroy_cis_cache(skt);
+			send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
+		} else {
+			send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
+		}
+		skt->socket.flags &= ~SS_DEBOUNCED;
+	} else
+		socket_shutdown(skt);
+
+	skt->state &= ~SOCKET_SUSPEND;
+
+	return CS_SUCCESS;
+}
+
+static int pccardd(void *__skt)
+{
+	socket_info_t *skt = __skt;
+	DECLARE_WAITQUEUE(wait, current);
+
+	daemonize("pccardd");
+	skt->thread = current;
+	complete(&skt->thread_done);
+
+	add_wait_queue(&skt->thread_wait, &wait);
+	for (;;) {
+		unsigned long flags;
+		unsigned int events;
+
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		spin_lock_irqsave(&skt->thread_lock, flags);
+		events = skt->thread_events;
+		skt->thread_events = 0;
+		spin_unlock_irqrestore(&skt->thread_lock, flags);
+
+		if (events) {
+			down(&skt->skt_sem);
+			if (events & SS_DETECT && !(skt->state & SOCKET_SUSPEND)) {
+				int status;
+
+				get_socket_status(skt, &status);
+				if ((skt->state & SOCKET_PRESENT) &&
+				     !(status & SS_DETECT))
+					socket_shutdown(skt);
+				if (status & SS_DETECT)
+					socket_insert(skt);
+			}
+			if (events & SS_BATDEAD)
+				send_event(skt, CS_EVENT_BATTERY_DEAD, CS_EVENT_PRI_LOW);
+			if (events & SS_BATWARN)
+				send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW);
+			if (events & SS_READY)
+				send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW);
+			up(&skt->skt_sem);
+			continue;
+		}
+
+		schedule();
+		if (!skt->thread)
+			break;
+	}
+	remove_wait_queue(&skt->thread_wait, &wait);
+
+	socket_shutdown(skt);
+
+	complete_and_exit(&skt->thread_done, 0);
 }
 
 static void parse_events(void *info, u_int events)
 {
-    socket_info_t *s = info;
-    if (events & SS_DETECT) {
-	int status;
-
-	get_socket_status(s, &status);
-	if ((s->state & SOCKET_PRESENT) &&
-	    (!(s->state & SOCKET_SUSPEND) ||
-	     !(status & SS_DETECT)))
-	    do_shutdown(s);
-	if (status & SS_DETECT) {
-	    if (s->state & SOCKET_SETUP_PENDING) {
-		DEBUG(1, "cs: delaying pending setup\n");
-		return;
-	    }
-	    s->state |= SOCKET_SETUP_PENDING;
-	    if (s->state & SOCKET_SUSPEND)
-		cs_sleep(resume_delay);
-	    else
-		cs_sleep(setup_delay);
-	    s->socket.flags |= SS_DEBOUNCED;
-	    if (setup_socket(s) == 0)
-		s->state &= ~SOCKET_SETUP_PENDING;
-	    s->socket.flags &= ~SS_DEBOUNCED;
-	}
-    }
-    if (events & SS_BATDEAD)
-	send_event(s, CS_EVENT_BATTERY_DEAD, CS_EVENT_PRI_LOW);
-    if (events & SS_BATWARN)
-	send_event(s, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW);
-    if (events & SS_READY) {
-	if (!(s->state & SOCKET_RESET_PENDING))
-	    send_event(s, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW);
-	else DEBUG(1, "cs: ready change during reset\n");
-    }
+	socket_info_t *s = info;
+
+	spin_lock(&s->thread_lock);
+	s->thread_events |= events;
+	spin_unlock(&s->thread_lock);
+
+	wake_up(&s->thread_wait);
 } /* parse_events */
 
 /*======================================================================
@@ -727,27 +813,18 @@
     
 ======================================================================*/
 
-void pcmcia_suspend_socket (socket_info_t *s)
+void pcmcia_suspend_socket (socket_info_t *skt)
 {
-    if ((s->state & SOCKET_PRESENT) && !(s->state & SOCKET_SUSPEND)) {
-	send_event(s, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
-	suspend_socket(s);
-	s->state |= SOCKET_SUSPEND;
-    }
+	down(&skt->skt_sem);
+	socket_suspend(skt);
+	up(&skt->skt_sem);
 }
 
-void pcmcia_resume_socket (socket_info_t *s)
+void pcmcia_resume_socket (socket_info_t *skt)
 {
-    int	stat;
-
-    /* Do this just to reinitialize the socket */
-    init_socket(s);
-    get_socket_status(s, &stat);
-
-    /* If there was or is a card here, we need to do something
-    about it... but parse_events will sort it all out. */
-    if ((s->state & SOCKET_PRESENT) || (stat & SS_DETECT))
-	parse_events(s, SS_DETECT);
+	down(&skt->skt_sem);
+	socket_resume(skt);
+	up(&skt->skt_sem);
 }
 
 
@@ -1463,15 +1540,8 @@
 
     s = socket_table[ns];
     if (++s->real_clients == 1) {
-	int status;
 	register_callback(s, &parse_events, s);
-	get_socket_status(s, &status);
-	if ((status & SS_DETECT) &&
-	    !(s->state & SOCKET_SETUP_PENDING)) {
-	    s->state |= SOCKET_SETUP_PENDING;
-	    if (setup_socket(s) == 0)
-		    s->state &= ~SOCKET_SETUP_PENDING;
-	}
+	parse_events(s, SS_DETECT);
     }
 
     *handle = client;
@@ -2024,30 +2094,44 @@
 
 int pcmcia_reset_card(client_handle_t handle, client_req_t *req)
 {
-    int i, ret;
-    socket_info_t *s;
+	socket_info_t *skt;
+	int ret;
     
-    if (CHECK_HANDLE(handle))
-	return CS_BAD_HANDLE;
-    i = handle->Socket; s = socket_table[i];
-    if (!(s->state & SOCKET_PRESENT))
-	return CS_NO_CARD;
-    if (s->state & SOCKET_RESET_PENDING)
-	return CS_IN_USE;
-    s->state |= SOCKET_RESET_PENDING;
+	if (CHECK_HANDLE(handle))
+		return CS_BAD_HANDLE;
+	DEBUG(1, "cs: resetting socket %d\n", handle->Socket);
+	skt = SOCKET(handle);
+
+	down(&skt->skt_sem);
+	do {
+		if (!(skt->state & SOCKET_PRESENT)) {
+			ret = CS_NO_CARD;
+			break;
+		}
+		if (skt->state & SOCKET_SUSPEND) {
+			ret = CS_IN_USE;
+			break;
+		}
+		if (skt->state & SOCKET_CARDBUS) {
+			ret = CS_UNSUPPORTED_FUNCTION;
+			break;
+		}
 
-    ret = send_event(s, CS_EVENT_RESET_REQUEST, CS_EVENT_PRI_LOW);
-    if (ret != 0) {
-	s->state &= ~SOCKET_RESET_PENDING;
-	handle->event_callback_args.info = (void *)(u_long)ret;
-	EVENT(handle, CS_EVENT_RESET_COMPLETE, CS_EVENT_PRI_LOW);
-    } else {
-	DEBUG(1, "cs: resetting socket %d\n", i);
-	send_event(s, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW);
-	s->reset_handle = handle;
-	reset_socket(s);
-    }
-    return CS_SUCCESS;
+		ret = send_event(skt, CS_EVENT_RESET_REQUEST, CS_EVENT_PRI_LOW);
+		if (ret == 0) {
+			send_event(skt, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW);
+			if (socket_reset(skt) == CS_SUCCESS)
+				send_event(skt, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW);
+		}
+
+		handle->event_callback_args.info = (void *)(u_long)ret;
+		EVENT(handle, CS_EVENT_RESET_COMPLETE, CS_EVENT_PRI_LOW);
+
+		ret = CS_SUCCESS;
+	} while (0);
+	up(&skt->skt_sem);
+
+	return ret;
 } /* reset_card */
 
 /*======================================================================
@@ -2059,42 +2143,56 @@
 
 int pcmcia_suspend_card(client_handle_t handle, client_req_t *req)
 {
-    int i;
-    socket_info_t *s;
+	socket_info_t *skt;
+	int ret;
     
-    if (CHECK_HANDLE(handle))
-	return CS_BAD_HANDLE;
-    i = handle->Socket; s = socket_table[i];
-    if (!(s->state & SOCKET_PRESENT))
-	return CS_NO_CARD;
-    if (s->state & SOCKET_SUSPEND)
-	return CS_IN_USE;
-
-    DEBUG(1, "cs: suspending socket %d\n", i);
-    send_event(s, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
-    suspend_socket(s);
-    s->state |= SOCKET_SUSPEND;
+	if (CHECK_HANDLE(handle))
+		return CS_BAD_HANDLE;
+	DEBUG(1, "cs: suspending socket %d\n", handle->Socket);
+	skt = SOCKET(handle);
+
+	down(&skt->skt_sem);
+	do {
+		if (!(skt->state & SOCKET_PRESENT)) {
+			ret = CS_NO_CARD;
+			break;
+		}
+		if (skt->state & SOCKET_CARDBUS) {
+			ret = CS_UNSUPPORTED_FUNCTION;
+			break;
+		}
+		ret = socket_suspend(skt);
+	} while (0);
+	up(&skt->skt_sem);
 
-    return CS_SUCCESS;
+	return ret;
 } /* suspend_card */
 
 int pcmcia_resume_card(client_handle_t handle, client_req_t *req)
 {
-    int i;
-    socket_info_t *s;
+	socket_info_t *skt;
+	int ret;
     
-    if (CHECK_HANDLE(handle))
-	return CS_BAD_HANDLE;
-    i = handle->Socket; s = socket_table[i];
-    if (!(s->state & SOCKET_PRESENT))
-	return CS_NO_CARD;
-    if (!(s->state & SOCKET_SUSPEND))
-	return CS_IN_USE;
-
-    DEBUG(1, "cs: waking up socket %d\n", i);
-    setup_socket(s);
+	if (CHECK_HANDLE(handle))
+		return CS_BAD_HANDLE;
+	DEBUG(1, "cs: waking up socket %d\n", handle->Socket);
+	skt = SOCKET(handle);
+
+	down(&skt->skt_sem);
+	do {
+		if (!(skt->state & SOCKET_PRESENT)) {
+			ret = CS_NO_CARD;
+			break;
+		}
+		if (skt->state & SOCKET_CARDBUS) {
+			ret = CS_UNSUPPORTED_FUNCTION;
+			break;
+		}
+		ret = socket_resume(skt);
+	} while (0);
+	up(&skt->skt_sem);
 
-    return CS_SUCCESS;
+	return ret;
 } /* resume_card */
 
 /*======================================================================
@@ -2105,57 +2203,58 @@
 
 int pcmcia_eject_card(client_handle_t handle, client_req_t *req)
 {
-    int i, ret;
-    socket_info_t *s;
-    u_long flags;
+	socket_info_t *skt;
+	int ret;
     
-    if (CHECK_HANDLE(handle))
-	return CS_BAD_HANDLE;
-    i = handle->Socket; s = socket_table[i];
-    if (!(s->state & SOCKET_PRESENT))
-	return CS_NO_CARD;
+	if (CHECK_HANDLE(handle))
+		return CS_BAD_HANDLE;
+	DEBUG(1, "cs: user eject request on socket %d\n", handle->Socket);
+	skt = SOCKET(handle);
+
+	down(&skt->skt_sem);
+	do {
+		if (!(skt->state & SOCKET_PRESENT)) {
+			ret = CS_NO_CARD;
+			break;
+		}
 
-    DEBUG(1, "cs: user eject request on socket %d\n", i);
+		ret = send_event(skt, CS_EVENT_EJECTION_REQUEST, CS_EVENT_PRI_LOW);
+		if (ret != 0)
+			break;
 
-    ret = send_event(s, CS_EVENT_EJECTION_REQUEST, CS_EVENT_PRI_LOW);
-    if (ret != 0)
-	return ret;
+		socket_shutdown(skt);
+		ret = CS_SUCCESS;
+	} while (0);
+	up(&skt->skt_sem);
 
-    spin_lock_irqsave(&s->lock, flags);
-    do_shutdown(s);
-    spin_unlock_irqrestore(&s->lock, flags);
-    
-    return CS_SUCCESS;
-    
+	return ret;
 } /* eject_card */
 
 int pcmcia_insert_card(client_handle_t handle, client_req_t *req)
 {
-    int i, status;
-    socket_info_t *s;
-    u_long flags;
-    
-    if (CHECK_HANDLE(handle))
-	return CS_BAD_HANDLE;
-    i = handle->Socket; s = socket_table[i];
-    if (s->state & SOCKET_PRESENT)
-	return CS_IN_USE;
-
-    DEBUG(1, "cs: user insert request on socket %d\n", i);
+	socket_info_t *skt;
+	int ret;
 
-    spin_lock_irqsave(&s->lock, flags);
-    if (!(s->state & SOCKET_SETUP_PENDING)) {
-	s->state |= SOCKET_SETUP_PENDING;
-	spin_unlock_irqrestore(&s->lock, flags);
-	get_socket_status(s, &status);
-	if ((status & SS_DETECT) == 0 || (setup_socket(s) == 0)) {
-	    s->state &= ~SOCKET_SETUP_PENDING;
-	    return CS_NO_CARD;
-	}
-    } else
-	spin_unlock_irqrestore(&s->lock, flags);
+	if (CHECK_HANDLE(handle))
+		return CS_BAD_HANDLE;
+	DEBUG(1, "cs: user insert request on socket %d\n", handle->Socket);
+	skt = SOCKET(handle);
+
+	down(&skt->skt_sem);
+	do {
+		if (skt->state & SOCKET_PRESENT) {
+			ret = CS_IN_USE;
+			break;
+		}
+		if (socket_insert(skt) == CS_NO_CARD) {
+			ret = CS_NO_CARD;
+			break;
+		}
+		ret = CS_SUCCESS;
+	} while (0);
+	up(&skt->skt_sem);
 
-    return CS_SUCCESS;
+	return ret;
 } /* insert_card */
 
 /*======================================================================
diff -u orig/drivers/pcmcia/cs_internal.h linux/drivers/pcmcia/cs_internal.h
--- orig/drivers/pcmcia/cs_internal.h	Sat Apr 26 23:01:03 2003
+++ linux/drivers/pcmcia/cs_internal.h	Sat Apr 26 21:34:59 2003
@@ -133,7 +133,6 @@
     u_short			lock_count;
     client_handle_t		clients;
     u_int			real_clients;
-    client_handle_t		reset_handle;
     pccard_mem_map		cis_mem;
     u_char			*cis_virt;
     config_t			*config;
@@ -155,6 +154,14 @@
 #ifdef CONFIG_PROC_FS
     struct proc_dir_entry	*proc;
 #endif
+
+    struct semaphore		skt_sem;	/* protects socket h/w state */
+
+    struct task_struct		*thread;
+    struct completion		thread_done;
+    wait_queue_head_t		thread_wait;
+    spinlock_t			thread_lock;	/* protects thread_events */
+    unsigned int		thread_events;
 } socket_info_t;
 
 /* Flags in config state */

-- 
Russell King (rmk@arm.linux.org.uk)                The developer of ARM Linux
             http://www.arm.linux.org.uk/personal/aboutme.html


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2003-04-30 10:58 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2003-04-29 14:17 [CFT] Fix PCMCIA deadlock (rev. 2) Russell King
2003-04-30 11:10 ` Felipe Alfaro Solana

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).