25-akpm/drivers/net/wireless/orinoco_cs.c | 1 25-akpm/drivers/pcmcia/cs.c | 827 +++++++++++++++++++++--------- 25-akpm/drivers/pcmcia/cs_internal.h | 18 25-akpm/drivers/pcmcia/rsrc_mgr.c | 20 25-akpm/include/pcmcia/bus_ops.h | 2 25-akpm/include/pcmcia/driver_ops.h | 2 25-akpm/include/pcmcia/ds.h | 2 7 files changed, 617 insertions(+), 255 deletions(-) diff -puN drivers/net/wireless/orinoco_cs.c~pcmcia-20030421 drivers/net/wireless/orinoco_cs.c --- 25/drivers/net/wireless/orinoco_cs.c~pcmcia-20030421 Mon Apr 21 17:38:29 2003 +++ 25-akpm/drivers/net/wireless/orinoco_cs.c Mon Apr 21 17:38:29 2003 @@ -35,7 +35,6 @@ #include #include #include -#include #include #include diff -puN drivers/pcmcia/cs.c~pcmcia-20030421 drivers/pcmcia/cs.c --- 25/drivers/pcmcia/cs.c~pcmcia-20030421 Mon Apr 21 17:38:29 2003 +++ 25-akpm/drivers/pcmcia/cs.c Mon Apr 21 17:38:29 2003 @@ -82,6 +82,8 @@ #define OPTIONS PCI_OPT CB_OPT PM_OPT #endif +#undef EVENT_DEBUG + static const char *release = "Linux Kernel Card Services " CS_RELEASE; static const char *options = "options: " OPTIONS; @@ -127,6 +129,19 @@ socket_state_t dead_socket = { socket_t sockets = 0; socket_info_t *socket_table[MAX_SOCK]; +/* + * Our private work queue. We need this because we will be running + * driver probe and remove functions from our workqueue. If we use + * keventd, and their remove function does a flush_scheduled_work(), + * we will deadlock. + * + * The unfortunate side effect of this is that we will have one + * pcmcia thread per CPU; unfortunately there doesn't seem to be a + * way of creating just one thread with the current workqueue + * implementation. + */ +static struct workqueue_struct *pcmcia_wq; + #ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_pccard = NULL; #endif @@ -302,11 +317,10 @@ static int proc_read_clients(char *buf, ======================================================================*/ -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 void sm_init(socket_info_t *s); +static void sm_exit(socket_info_t *s); #define to_class_data(dev) dev->class_data @@ -343,7 +357,8 @@ int pcmcia_register_socket(struct device s->cis_mem.speed = cis_speed; s->erase_busy.next = s->erase_busy.prev = &s->erase_busy; spin_lock_init(&s->lock); - + sm_init(s); + /* TBD: remove usage of socket_table, use class_for_each_dev instead */ for (j = 0; j < sockets; j++) if (socket_table[j] == NULL) break; @@ -406,8 +421,8 @@ void pcmcia_unregister_socket(struct dev remove_proc_entry(name, proc_pccard); } #endif - - shutdown_socket(s); + + sm_exit(s); release_cis_mem(s); while (s->clients) { client = s->clients; @@ -449,15 +464,6 @@ static void free_regions(memory_handle_t 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 +511,9 @@ static void shutdown_socket(socket_info_ 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,62 +544,556 @@ static int send_event(socket_info_t *s, return ret; } /* send_event */ -static void do_shutdown(socket_info_t *s) +/* + * Event mask. + */ +#define EV_TIMER (1<<0) +#define EV_DETECT (1<<1) +#define EV_READY (1<<2) +#define EV_SUSPEND (1<<3) +#define EV_RESUME (1<<4) +#define EV_RESOURCES (1<<5) +#define EV_RESET (1<<6) +#define EV_EJECT (1<<7) +#define EV_INSERT (1<<8) +#define EV_FORCE (1<<31) + +static void state_error(socket_info_t *s, unsigned int status, unsigned int events); +static void state_empty(socket_info_t *s, unsigned int status, unsigned int events); +static void state_setup(socket_info_t *s, unsigned int status, unsigned int events); +static void state_reset(socket_info_t *s, unsigned int status, unsigned int events); +static void state_wait_ready(socket_info_t *s, unsigned int status, unsigned int events); +static void state_cardpresent(socket_info_t *s, unsigned int status, unsigned int events); +static void state_suspend(socket_info_t *s, unsigned int status, unsigned int events); +static void state_removed(socket_info_t *s, unsigned int status, unsigned int events); +static void state_shutdown(socket_info_t *s, unsigned int status, unsigned int events); + +static inline void __sm_event_queue(socket_info_t *s, unsigned int event) { - 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); + unsigned long flags; + + spin_lock_irqsave(&s->sm.lock, flags); + s->sm.event_pending |= event; + spin_unlock_irqrestore(&s->sm.lock, flags); } -static void parse_events(void *info, u_int events) +static inline void sm_event_clear(socket_info_t *s, unsigned int event) +{ + unsigned long flags; + + spin_lock_irqsave(&s->sm.lock, flags); + s->sm.event_pending &= ~event; + spin_unlock_irqrestore(&s->sm.lock, flags); +} + +static void sm_event_ack(socket_info_t *s, unsigned int events) +{ + unsigned long flags; + + if (events & EV_RESET) + s->state &= ~SOCKET_RESET_PENDING; + if (events & EV_DETECT) + s->state &= ~(SOCKET_SETUP_PENDING|SOCKET_RESET_PENDING); + + spin_lock_irqsave(&s->sm.lock, flags); + s->sm.event_ack |= events; + s->sm.event_pending &= ~events; + spin_unlock_irqrestore(&s->sm.lock, flags); + + if (events) + wake_up(&s->sm.wait_ack); +} + +static inline void sm_set_timer(socket_info_t *s, unsigned int cs) { - 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"); + sm_event_clear(s, EV_TIMER); + s->sm.event_mask |= EV_TIMER; + mod_timer(&s->sm.timer, jiffies + ((cs * HZ + 99) / 100)); +} + +/* + * Move to the next state. + */ +static void +sm_next(socket_info_t *s, + void (*fn)(socket_info_t *,unsigned int,unsigned int), + unsigned int mask) +{ + s->sm.event_mask = mask | EV_FORCE; + s->sm.func = fn; + __sm_event_queue(s, EV_FORCE); +} + + +static void sm_error(socket_info_t *s, const char *fmt, ...) +{ + 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", s, buf); + + sm_event_clear(s, EV_DETECT|EV_READY|EV_RESET); + sm_next(s, state_error, 0); +} + +/* + * We hit an error. Stop the state machine, but respond to + * suspend, resume, reset and resources events. + */ +static void state_error(socket_info_t *s, unsigned int status, unsigned int events) +{ + /* + * We hit an error. Ensure that the socket is off. + */ + s->socket.flags &= ~(SS_DEBOUNCED|SS_OUTPUT_ENA|SS_RESET|SS_IOCARD); + s->socket.Vcc = 0; + s->socket.Vpp = 0; + set_socket(s, &s->socket); + s->state &= ~SOCKET_SUSPEND; + + s->sm.event_mask = EV_RESET|EV_SUSPEND|EV_RESUME|EV_INSERT|EV_EJECT|EV_DETECT|EV_RESOURCES; + + /* + * If we received a reset event from the user, re-detect + * the card. This will automatically reset the card. + */ + if (events & (EV_RESET|EV_RESOURCES|EV_EJECT)) { + s->sm.event_mask = EV_DETECT|EV_INSERT|EV_SUSPEND; + s->sm.func = state_empty; + __sm_event_queue(s, EV_DETECT); + } + + sm_event_ack(s, events & (EV_SUSPEND|EV_RESUME|EV_INSERT|EV_DETECT)); +} + +/* + * The socket is empty. If a card is inserted (or we receive an + * insert request,) setup and reset the socket. If we receive a + * suspend, go to the suspend state and acknowledge the suspend. + */ +static void +state_empty(socket_info_t *s, unsigned int status, unsigned int events) +{ + if (events & (EV_DETECT|EV_INSERT) && status & SS_DETECT) { + s->socket.flags = SS_DEBOUNCED; + s->sm.tries = 100; + s->sm.func = state_setup; + s->sm.event_mask = 0; + + s->state |= SOCKET_SETUP_PENDING; + + sm_set_timer(s, setup_delay); 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"); - } -} /* parse_events */ + } + + if (events & EV_SUSPEND) { + s->sm.func = state_suspend; + s->sm.event_mask = EV_RESUME; + } + + /* Acknowledge all other events. */ + sm_event_ack(s, events); +} + +/* + * The setup/resume delay has expired. Wait for the socket hardware to + * debounce the voltage sense/card type sense, claim a resource for the + * CIS, and apply power to the card. Generally, we receive timer events, + * unless we are unable to grab resources for CIS. + */ +static void +state_setup(socket_info_t *s, unsigned int status, unsigned int events) +{ + int ret; + + if (events & EV_RESET) + sm_event_ack(s, EV_RESET); + + if (!(status & SS_DETECT) || events & EV_EJECT) { + sm_next(s, state_removed, EV_DETECT|EV_INSERT); + return; + } + + if (events & EV_SUSPEND) { + sm_next(s, state_suspend, EV_SUSPEND); + return; + } + + /* + * Some sockets debounce the voltage sense themselves. Wait for + * the debounce period to end, indicated by SS_PENDING clear. + */ + if (status & SS_PENDING) { + if (events & EV_TIMER) { + if (--s->sm.tries) { + sm_set_timer(s, 10); + } else { + sm_error(s, "voltage interrogation timed out\n"); + } + } + return; + } + + if (status & SS_CARDBUS) { + s->state |= SOCKET_CARDBUS; +#ifndef CONFIG_CARDBUS + sm_error(s, "cardbus cards are not supported\n"); + return; +#endif + } + + s->state |= SOCKET_PRESENT; + + /* + * Decode the card voltage requirements, and apply power to the card. + */ + if (status & SS_3VCARD) + s->socket.Vcc = s->socket.Vpp = 33; + else if (!(status & SS_XVCARD)) + s->socket.Vcc = s->socket.Vpp = 50; + else { + sm_error(s, "unsupported voltage key\n"); + return; + } + + set_socket(s, &s->socket); + + /* + * Wait "vcc_settle" for the supply to stabilise, and reset + * the card. Note that we only want to see timer events. + */ + s->sm.event_mask = 0; + s->sm.func = state_reset; + sm_set_timer(s, vcc_settle); +} + +/* + * Enable the socket tri-state drivers, and reset the card. + * Note that we also clear any pending reset events. + */ +static void state_reset(socket_info_t *s, unsigned int status, unsigned int events) +{ + if (!(status & SS_DETECT)) { + sm_next(s, state_removed, 0); + return; + } + + sm_event_clear(s, EV_READY); + + 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); + + s->sm.tries = unreset_limit; + s->sm.func = state_wait_ready; + sm_set_timer(s, unreset_delay); +} + +/* + * Wait for the card to signal that it is ready for us to read things + * like the CIS. We also allow the user to request the card to be + * reset. We only deal with eject, reset and ready events here. + */ +static void state_wait_ready(socket_info_t *s, unsigned int status, unsigned int events) +{ + if (!(status & SS_DETECT) || events & EV_EJECT) { + sm_next(s, state_removed, 0); + return; + } + + if (!(status & SS_READY) && events & EV_TIMER) { + if (--s->sm.tries) { + s->sm.event_mask = EV_EJECT|EV_READY; + sm_set_timer(s, unreset_check); + } else { + s->state &= ~EVENT_MASK; + sm_error(s, "timed out during reset. Try increasing setup_delay.\n"); + } + return; + } + + s->sm.event_mask = EV_EJECT|EV_SUSPEND|EV_RESET|EV_READY|EV_DETECT; + s->sm.func = state_cardpresent; + + if (s->state & SOCKET_SUSPEND) { + s->state &= ~(SOCKET_SUSPEND|SOCKET_SETUP_PENDING); + if (verify_cis_cache(s) != 0) { + __sm_event_queue(s, EV_DETECT); + sm_next(s, state_removed, 0); + } else + send_event(s, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW); + events = EV_RESUME|EV_INSERT; + } 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); + events = EV_DETECT|EV_INSERT; + } else if (s->state & SOCKET_RESET_PENDING) { + 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); + } + events = EV_RESET; + } + + sm_event_ack(s, events|EV_READY); +} + +/* + * The card is present, and has indicated that it is ready. We're + * ready to announce the device to the world. + */ +static void state_cardpresent(socket_info_t *s, unsigned int status, unsigned int events) +{ + if (!(status & SS_DETECT) || events & EV_EJECT) { + sm_next(s, state_removed, 0); + return; + } + + if (events & EV_SUSPEND) { + sm_next(s, state_suspend, EV_SUSPEND); + return; + } + + if (events & EV_RESET) { + sm_next(s, state_reset, EV_RESET); + return; + } + + if (events & EV_READY) + send_event(s, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW); + + sm_event_ack(s, events); +} + +/* + * The socket was suspended. Handle resume and eject events. + * Clear any pending detect events on resume. + * + * Handles: EV_SUSPEND|EV_EJECT|EV_RESUME + */ +static void state_suspend(socket_info_t *s, unsigned int status, unsigned int events) +{ + if (events & EV_SUSPEND) { + send_event(s, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW); + suspend_socket(s); + s->state |= SOCKET_SUSPEND; + s->sm.event_mask = EV_RESUME|EV_EJECT; + sm_event_ack(s, EV_DETECT|EV_SUSPEND); + } + + if (events & EV_EJECT) { + sm_next(s, state_removed, 0); + } else if (events & EV_RESUME) { + /* Do this just to reinitialize the socket */ + init_socket(s); + if (status & SS_DETECT) { + s->socket.flags = SS_DEBOUNCED; + s->sm.tries = 100; + s->sm.func = state_setup; + s->sm.event_mask = 0; + sm_set_timer(s, resume_delay); + sm_event_ack(s, EV_DETECT); + } else if (s->state & SOCKET_PRESENT) { + s->sm.func = state_removed; + } else { + s->sm.func = state_empty; + s->sm.event_mask = EV_DETECT|EV_RESET|EV_SUSPEND; + sm_event_ack(s, EV_RESUME|EV_DETECT); + } + } +} + +/* + * We come here when the socket becomes empty. + * Which events are acknowledged depends on the event mask. + */ +static void state_removed(socket_info_t *s, unsigned int status, unsigned int events) +{ + client_t *client; + + sm_event_ack(s, events); + + 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; + } + + s->sm.func = state_shutdown; + s->sm.event_mask = 0; + sm_set_timer(s, shutdown_delay); +} + +/* + * Complete socket shutdown. + * + * Handles: EV_TIMER + */ +static void state_shutdown(socket_info_t *s, unsigned int status, unsigned int events) +{ + s->state &= ~SOCKET_PRESENT; + shutdown_socket(s); + + s->sm.func = state_empty; + s->sm.event_mask = EV_DETECT|EV_RESET|EV_SUSPEND|EV_INSERT; + + sm_event_ack(s, EV_EJECT); +} + +static void sm_do_work(void *data) +{ + socket_info_t *s = data; + unsigned int events; + + do { + int status; + + /* + * Get pending events, and then read the status. + * Always clear the timer. + */ + spin_lock_irq(&s->sm.lock); + events = s->sm.event_pending & s->sm.event_mask; + spin_unlock_irq(&s->sm.lock); + + if (events == 0) + break; + + get_socket_status(s, &status); + +#ifdef EVENT_DEBUG + printk("cs: sock %p old smfunc %p mask 0x%08x pending 0x%08x " + "sockstate 0x%08x status 0x%08x delivering 0x%08x\n", + s, s->sm.func, s->sm.event_mask, s->sm.event_pending, + s->state, status, events); +#endif + + if (events) + s->sm.func(s, status, events); + +#ifdef EVENT_DEBUG + printk("cs: sock %p new smfunc %p mask 0x%08x pending 0x%08x " + "sockstate 0x%08x\n", + s, s->sm.func, s->sm.event_mask, s->sm.event_pending, + s->state); +#endif + } while (events); +} + +static void sm_event_queue(socket_info_t *s, unsigned int event) +{ + __sm_event_queue(s, event); + if (event & s->sm.event_mask) + queue_work(pcmcia_wq, &s->sm.work); +} + +static void sm_timer_expire(unsigned long data) +{ + socket_info_t *s = (socket_info_t *)data; + sm_event_queue(s, EV_TIMER); +} + +static void sm_init(socket_info_t *s) +{ + s->sm.event_mask = EV_DETECT|EV_RESET|EV_SUSPEND; + s->sm.func = state_empty; + + spin_lock_init(&s->sm.lock); + + init_timer(&s->sm.timer); + s->sm.timer.data = (unsigned long)s; + s->sm.timer.function = sm_timer_expire; + + init_waitqueue_head(&s->sm.wait_ack); + + INIT_WORK(&s->sm.work, sm_do_work, s); +} + +static void sm_exit(socket_info_t *s) +{ + del_timer_sync(&s->sm.timer); + flush_scheduled_work(); + shutdown_socket(s); +} + +/** + * sm_event_wait - queue an event and wait for acknowledgement + * @s: socket info structure describing socket + * @event: event to queue. + * + * Queue an event for the CS state machine to process, and wait + * for the state machine to acknowledge. + * + * Restrictions: If this function is called from the kernel + * event thread, we will deadlock. + */ +static void sm_event_wait(socket_info_t *s, unsigned int event) +{ +#if 0 + int is_keventd = current_is_keventd(); + WARN_ON(is_keventd); + if (is_keventd) + return; +#endif + + s->sm.event_ack &= ~event; + sm_event_queue(s, event); + wait_event(s->sm.wait_ack, s->sm.event_ack & event); +} + +void pcmcia_resources_changed(void) +{ + int i; + + for (i = 0; i < sockets; i++) { + socket_info_t *s = socket_table[i]; + if (!s) + continue; + sm_event_queue(s, EV_RESOURCES); +#ifdef CONFIG_CARDBUS + cb_release_cis_mem(s); +#endif + } +} + +static void parse_events(void *info, u_int events) +{ + socket_info_t *s = info; + unsigned int mask = 0; + + if (events & SS_DETECT) + mask |= EV_DETECT; + if (events & SS_READY) + mask |= EV_READY; + + if (mask) + sm_event_queue(s, mask); + + 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); +} /*====================================================================== @@ -729,25 +1106,12 @@ static void parse_events(void *info, u_i void pcmcia_suspend_socket (socket_info_t *s) { - 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; - } + sm_event_wait(s, EV_SUSPEND); } void pcmcia_resume_socket (socket_info_t *s) { - 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); + sm_event_wait(s, EV_RESUME); } @@ -1446,6 +1810,7 @@ int pcmcia_register_client(client_handle client_t *client; socket_info_t *s; socket_t ns; + int nr_clients; /* Look for unbound client with matching dev_info */ client = NULL; @@ -1462,17 +1827,7 @@ int pcmcia_register_client(client_handle return CS_OUT_OF_RESOURCE; 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; - } - } + nr_clients = ++s->real_clients; *handle = client; client->state &= ~CLIENT_UNBOUND; @@ -1512,6 +1867,10 @@ int pcmcia_register_client(client_handle else client->PendingEvents |= CS_EVENT_CARD_INSERTION; } + if (nr_clients == 1) { + register_callback(s, &parse_events, s); + sm_event_queue(s, EV_DETECT); + } return CS_SUCCESS; } /* register_client */ @@ -2044,8 +2403,10 @@ int pcmcia_reset_card(client_handle_t ha } 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); + + sm_event_wait(s, EV_RESET); } return CS_SUCCESS; } /* reset_card */ @@ -2071,9 +2432,7 @@ int pcmcia_suspend_card(client_handle_t 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; + sm_event_wait(s, EV_SUSPEND); return CS_SUCCESS; } /* suspend_card */ @@ -2092,7 +2451,7 @@ int pcmcia_resume_card(client_handle_t h return CS_IN_USE; DEBUG(1, "cs: waking up socket %d\n", i); - setup_socket(s); + sm_event_wait(s, EV_RESUME); return CS_SUCCESS; } /* resume_card */ @@ -2107,7 +2466,6 @@ int pcmcia_eject_card(client_handle_t ha { int i, ret; socket_info_t *s; - u_long flags; if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; @@ -2121,9 +2479,7 @@ int pcmcia_eject_card(client_handle_t ha if (ret != 0) return ret; - spin_lock_irqsave(&s->lock, flags); - do_shutdown(s); - spin_unlock_irqrestore(&s->lock, flags); + sm_event_wait(s, EV_EJECT); return CS_SUCCESS; @@ -2131,9 +2487,8 @@ int pcmcia_eject_card(client_handle_t ha int pcmcia_insert_card(client_handle_t handle, client_req_t *req) { - int i, status; + int i; socket_info_t *s; - u_long flags; if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; @@ -2143,17 +2498,9 @@ int pcmcia_insert_card(client_handle_t h DEBUG(1, "cs: user insert request on socket %d\n", i); - 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); + sm_event_wait(s, EV_INSERT); + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; return CS_SUCCESS; } /* insert_card */ @@ -2433,6 +2780,10 @@ EXPORT_SYMBOL(pcmcia_socket_class); static int __init init_pcmcia_cs(void) { + pcmcia_wq = create_workqueue("pcmcia"); + if (!pcmcia_wq) + return -ENOMEM; + printk(KERN_INFO "%s\n", release); printk(KERN_INFO " %s\n", options); DEBUG(0, "%s\n", version); @@ -2454,6 +2805,8 @@ static void __exit exit_pcmcia_cs(void) #endif release_resource_db(); devclass_unregister(&pcmcia_socket_class); + + destroy_workqueue(pcmcia_wq); } subsys_initcall(init_pcmcia_cs); diff -puN drivers/pcmcia/cs_internal.h~pcmcia-20030421 drivers/pcmcia/cs_internal.h --- 25/drivers/pcmcia/cs_internal.h~pcmcia-20030421 Mon Apr 21 17:38:29 2003 +++ 25-akpm/drivers/pcmcia/cs_internal.h Mon Apr 21 17:38:29 2003 @@ -118,9 +118,24 @@ typedef struct config_t { #define MAX_CIS_TABLE 64 #define MAX_CIS_DATA 512 +struct socket_info_t; + +struct sm_state { + void (*func)(struct socket_info_t *, unsigned int, unsigned int); + spinlock_t lock; + unsigned int event_pending; + unsigned int event_mask; + unsigned int event_ack; + unsigned int tries; + wait_queue_head_t wait_ack; + struct timer_list timer; + struct work_struct work; +}; + typedef struct socket_info_t { spinlock_t lock; struct pccard_operations * ss_entry; + struct sm_state sm; u_int sock; socket_state_t socket; socket_cap_t cap; @@ -216,6 +231,9 @@ int validate_cis(client_handle_t handle, int replace_cis(client_handle_t handle, cisdump_t *cis); int read_tuple(client_handle_t handle, cisdata_t code, void *parse); +/* In cs.c */ +void pcmcia_resources_changed(void); + /* In bulkmem.c */ void retry_erase_list(struct erase_busy_t *list, u_int cause); int get_first_region(client_handle_t handle, region_info_t *rgn); diff -puN drivers/pcmcia/rsrc_mgr.c~pcmcia-20030421 drivers/pcmcia/rsrc_mgr.c --- 25/drivers/pcmcia/rsrc_mgr.c~pcmcia-20030421 Mon Apr 21 17:38:29 2003 +++ 25-akpm/drivers/pcmcia/rsrc_mgr.c Mon Apr 21 17:38:29 2003 @@ -499,14 +499,16 @@ void validate_mem(socket_info_t *s) void validate_mem(socket_info_t *s) { - resource_map_t *m; + resource_map_t *m, *n; static int done = 0; if (probe_mem && done++ == 0) { down(&rsrc_sem); - for (m = mem_db.next; m != &mem_db; m = m->next) + for (m = mem_db.next; m != &mem_db; m = n) { + n = m->next; if (do_mem_probe(m->base, m->num, s)) break; + } up(&rsrc_sem); } } @@ -724,7 +726,7 @@ void undo_irq(u_int Attributes, int irq) static int adjust_memory(adjust_t *adj) { u_long base, num; - int i, ret; + int ret; base = adj->resource.memory.Base; num = adj->resource.memory.Size; @@ -740,20 +742,14 @@ static int adjust_memory(adjust_t *adj) break; case REMOVE_MANAGED_RESOURCE: ret = sub_interval(&mem_db, base, num); - if (ret == CS_SUCCESS) { - for (i = 0; i < sockets; i++) { - release_cis_mem(socket_table[i]); -#ifdef CONFIG_CARDBUS - cb_release_cis_mem(socket_table[i]); -#endif - } - } break; default: ret = CS_UNSUPPORTED_FUNCTION; } up(&rsrc_sem); - + if (ret == CS_SUCCESS) + pcmcia_resources_changed(); + return ret; } diff -puN include/pcmcia/bus_ops.h~pcmcia-20030421 include/pcmcia/bus_ops.h --- 25/include/pcmcia/bus_ops.h~pcmcia-20030421 Mon Apr 21 17:38:29 2003 +++ 25-akpm/include/pcmcia/bus_ops.h Mon Apr 21 17:38:29 2003 @@ -1,2 +0,0 @@ -/* now empty */ -#warning please remove the reference to this file diff -puN include/pcmcia/driver_ops.h~pcmcia-20030421 include/pcmcia/driver_ops.h --- 25/include/pcmcia/driver_ops.h~pcmcia-20030421 Mon Apr 21 17:38:29 2003 +++ 25-akpm/include/pcmcia/driver_ops.h Mon Apr 21 17:38:29 2003 @@ -1,2 +0,0 @@ -/* now empty */ -#warning please remove the reference to this file diff -puN include/pcmcia/ds.h~pcmcia-20030421 include/pcmcia/ds.h --- 25/include/pcmcia/ds.h~pcmcia-20030421 Mon Apr 21 17:38:29 2003 +++ 25-akpm/include/pcmcia/ds.h Mon Apr 21 17:38:29 2003 @@ -31,7 +31,6 @@ #define _LINUX_DS_H #include -#include #include typedef struct tuple_parse_t { @@ -107,6 +106,7 @@ typedef union ds_ioctl_arg_t { #define DS_BIND_MTD _IOWR('d', 64, mtd_info_t) #ifdef __KERNEL__ +#include typedef struct dev_node_t { char dev_name[DEV_NAME_LEN]; _