Commit 77e62b64 authored by Russell King's avatar Russell King

[PCMCIA] Add per-socket thread to process socket events.

Add a per-socket kernel thread, which is responsible for processing
insertion, removal, battery status and ready status changes.  We
also add a semaphore to prevent multiple threads trying to change
the socket suspend/present state.

This will allows us to eliminate the per-socket work queues, and
will allow us to handle the card insertion without resources
cleanly.
parent ac062f74
...@@ -302,11 +302,8 @@ static int proc_read_clients(char *buf, char **start, off_t pos, ...@@ -302,11 +302,8 @@ static int proc_read_clients(char *buf, char **start, off_t pos,
======================================================================*/ ======================================================================*/
static int setup_socket(socket_info_t *); static int pccardd(void *__skt);
static void shutdown_socket(socket_info_t *); void pcmcia_unregister_socket(struct class_device *dev);
static void reset_socket(socket_info_t *);
static void unreset_socket(socket_info_t *);
static void parse_events(void *info, u_int events);
#define to_class_data(dev) dev->class_data #define to_class_data(dev) dev->class_data
...@@ -317,7 +314,7 @@ int pcmcia_register_socket(struct class_device *class_dev) ...@@ -317,7 +314,7 @@ int pcmcia_register_socket(struct class_device *class_dev)
{ {
struct pcmcia_socket_class_data *cls_d = class_get_devdata(class_dev); struct pcmcia_socket_class_data *cls_d = class_get_devdata(class_dev);
socket_info_t *s_info; socket_info_t *s_info;
unsigned int i, j; unsigned int i, j, ret;
if (!cls_d) if (!cls_d)
return -EINVAL; return -EINVAL;
...@@ -330,6 +327,7 @@ int pcmcia_register_socket(struct class_device *class_dev) ...@@ -330,6 +327,7 @@ int pcmcia_register_socket(struct class_device *class_dev)
memset(s_info, 0, cls_d->nsock * sizeof(socket_info_t)); memset(s_info, 0, cls_d->nsock * sizeof(socket_info_t));
cls_d->s_info = s_info; cls_d->s_info = s_info;
ret = 0;
/* socket initialization */ /* socket initialization */
for (i = 0; i < cls_d->nsock; i++) { for (i = 0; i < cls_d->nsock; i++) {
...@@ -344,7 +342,7 @@ int pcmcia_register_socket(struct class_device *class_dev) ...@@ -344,7 +342,7 @@ int pcmcia_register_socket(struct class_device *class_dev)
s->erase_busy.next = s->erase_busy.prev = &s->erase_busy; s->erase_busy.next = s->erase_busy.prev = &s->erase_busy;
INIT_LIST_HEAD(&s->cis_cache); INIT_LIST_HEAD(&s->cis_cache);
spin_lock_init(&s->lock); spin_lock_init(&s->lock);
/* TBD: remove usage of socket_table, use class_for_each_dev instead */ /* TBD: remove usage of socket_table, use class_for_each_dev instead */
for (j = 0; j < sockets; j++) for (j = 0; j < sockets; j++)
if (socket_table[j] == NULL) break; if (socket_table[j] == NULL) break;
...@@ -353,6 +351,20 @@ int pcmcia_register_socket(struct class_device *class_dev) ...@@ -353,6 +351,20 @@ int pcmcia_register_socket(struct class_device *class_dev)
init_socket(s); init_socket(s);
s->ss_entry->inquire_socket(s->sock, &s->cap); 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(class_dev);
break;
}
wait_for_completion(&s->thread_done);
BUG_ON(!s->thread);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
if (proc_pccard) { if (proc_pccard) {
char name[3]; char name[3];
...@@ -368,7 +380,7 @@ int pcmcia_register_socket(struct class_device *class_dev) ...@@ -368,7 +380,7 @@ int pcmcia_register_socket(struct class_device *class_dev)
} }
#endif #endif
} }
return 0; return ret;
} /* pcmcia_register_socket */ } /* pcmcia_register_socket */
...@@ -407,8 +419,12 @@ void pcmcia_unregister_socket(struct class_device *class_dev) ...@@ -407,8 +419,12 @@ void pcmcia_unregister_socket(struct class_device *class_dev)
remove_proc_entry(name, proc_pccard); remove_proc_entry(name, proc_pccard);
} }
#endif #endif
if (s->thread) {
shutdown_socket(s); init_completion(&s->thread_done);
s->thread = NULL;
wake_up(&s->thread_wait);
wait_for_completion(&s->thread_done);
}
release_cis_mem(s); release_cis_mem(s);
while (s->clients) { while (s->clients) {
client = s->clients; client = s->clients;
...@@ -450,15 +466,6 @@ static void free_regions(memory_handle_t *list) ...@@ -450,15 +466,6 @@ static void free_regions(memory_handle_t *list)
static int send_event(socket_info_t *s, event_t event, int priority); 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) static void shutdown_socket(socket_info_t *s)
{ {
client_t **c; client_t **c;
...@@ -505,132 +512,6 @@ static void shutdown_socket(socket_info_t *s) ...@@ -505,132 +512,6 @@ static void shutdown_socket(socket_info_t *s)
free_regions(&s->c_region); free_regions(&s->c_region);
} /* shutdown_socket */ } /* 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 The central event handler. Send_event() sends an event to all
...@@ -661,61 +542,266 @@ static int send_event(socket_info_t *s, event_t event, int priority) ...@@ -661,61 +542,266 @@ static int send_event(socket_info_t *s, event_t event, int priority)
return ret; return ret;
} /* send_event */ } /* send_event */
static void do_shutdown(socket_info_t *s) static void pcmcia_error(socket_info_t *skt, const char *fmt, ...)
{ {
client_t *client; static char buf[128];
if (s->state & SOCKET_SHUTDOWN_PENDING) va_list ap;
return; int len;
s->state |= SOCKET_SHUTDOWN_PENDING;
send_event(s, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH); va_start(ap, fmt);
for (client = s->clients; client; client = client->next) len = vsnprintf(buf, sizeof(buf), fmt, ap);
if (!(client->Attributes & INFO_MASTER_CLIENT)) va_end(ap);
client->state |= CLIENT_STALE; buf[len] = '\0';
if (s->state & (SOCKET_SETUP_PENDING|SOCKET_RESET_PENDING)) {
DEBUG(0, "cs: flushing pending setup\n"); printk(KERN_ERR "PCMCIA: socket %p: %s", skt, buf);
s->state &= ~EVENT_MASK;
}
cs_sleep(shutdown_delay);
s->state &= ~SOCKET_PRESENT;
shutdown_socket(s);
} }
static void parse_events(void *info, u_int events) #define cs_to_timeout(cs) (((cs) * HZ + 99) / 100)
static void socket_remove_drivers(socket_info_t *skt)
{ {
socket_info_t *s = info; client_t *client;
if (events & SS_DETECT) {
int status; send_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
get_socket_status(s, &status); for (client = skt->clients; client; client = client->next)
if ((s->state & SOCKET_PRESENT) && if (!(client->Attributes & INFO_MASTER_CLIENT))
(!(s->state & SOCKET_SUSPEND) || client->state |= CLIENT_STALE;
!(status & SS_DETECT))) }
do_shutdown(s);
if (status & SS_DETECT) { static void socket_shutdown(socket_info_t *skt)
if (s->state & SOCKET_SETUP_PENDING) { {
DEBUG(1, "cs: delaying pending setup\n"); socket_remove_drivers(skt);
return; set_current_state(TASK_UNINTERRUPTIBLE);
} schedule_timeout(cs_to_timeout(shutdown_delay));
s->state |= SOCKET_SETUP_PENDING; skt->state &= ~SOCKET_PRESENT;
if (s->state & SOCKET_SUSPEND) shutdown_socket(skt);
cs_sleep(resume_delay); }
else
cs_sleep(setup_delay); static int socket_reset(socket_info_t *skt)
s->socket.flags |= SS_DEBOUNCED; {
if (setup_socket(s) == 0) int status, i;
s->state &= ~SOCKET_SETUP_PENDING;
s->socket.flags &= ~SS_DEBOUNCED; 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));
} }
}
if (events & SS_BATDEAD) pcmcia_error(skt, "time out after reset.\n");
send_event(s, CS_EVENT_BATTERY_DEAD, CS_EVENT_PRI_LOW); return CS_GENERAL_FAILURE;
if (events & SS_BATWARN) }
send_event(s, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW);
if (events & SS_READY) { static int socket_setup(socket_info_t *skt, int initial_delay)
if (!(s->state & SOCKET_RESET_PENDING)) {
send_event(s, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW); int status, i;
else DEBUG(1, "cs: ready change during reset\n");
} 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;
spin_lock(&s->thread_lock);
s->thread_events |= events;
spin_unlock(&s->thread_lock);
wake_up(&s->thread_wait);
} /* parse_events */ } /* parse_events */
/*====================================================================== /*======================================================================
...@@ -727,27 +813,18 @@ static void parse_events(void *info, u_int events) ...@@ -727,27 +813,18 @@ static void parse_events(void *info, u_int events)
======================================================================*/ ======================================================================*/
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)) { down(&skt->skt_sem);
send_event(s, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW); socket_suspend(skt);
suspend_socket(s); up(&skt->skt_sem);
s->state |= SOCKET_SUSPEND;
}
} }
void pcmcia_resume_socket (socket_info_t *s) void pcmcia_resume_socket (socket_info_t *skt)
{ {
int stat; down(&skt->skt_sem);
socket_resume(skt);
/* Do this just to reinitialize the socket */ up(&skt->skt_sem);
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);
} }
...@@ -1461,15 +1538,8 @@ int pcmcia_register_client(client_handle_t *handle, client_reg_t *req) ...@@ -1461,15 +1538,8 @@ int pcmcia_register_client(client_handle_t *handle, client_reg_t *req)
s = socket_table[ns]; s = socket_table[ns];
if (++s->real_clients == 1) { if (++s->real_clients == 1) {
int status;
register_callback(s, &parse_events, s); register_callback(s, &parse_events, s);
get_socket_status(s, &status); parse_events(s, SS_DETECT);
if ((status & SS_DETECT) &&
!(s->state & SOCKET_SETUP_PENDING)) {
s->state |= SOCKET_SETUP_PENDING;
if (setup_socket(s) == 0)
s->state &= ~SOCKET_SETUP_PENDING;
}
} }
*handle = client; *handle = client;
...@@ -2022,30 +2092,44 @@ int pcmcia_request_window(client_handle_t *handle, win_req_t *req, window_handle ...@@ -2022,30 +2092,44 @@ int pcmcia_request_window(client_handle_t *handle, win_req_t *req, window_handle
int pcmcia_reset_card(client_handle_t handle, client_req_t *req) int pcmcia_reset_card(client_handle_t handle, client_req_t *req)
{ {
int i, ret; socket_info_t *skt;
socket_info_t *s; int ret;
if (CHECK_HANDLE(handle)) if (CHECK_HANDLE(handle))
return CS_BAD_HANDLE; return CS_BAD_HANDLE;
i = handle->Socket; s = socket_table[i]; DEBUG(1, "cs: resetting socket %d\n", handle->Socket);
if (!(s->state & SOCKET_PRESENT)) skt = SOCKET(handle);
return CS_NO_CARD;
if (s->state & SOCKET_RESET_PENDING) down(&skt->skt_sem);
return CS_IN_USE; do {
s->state |= SOCKET_RESET_PENDING; 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); ret = send_event(skt, CS_EVENT_RESET_REQUEST, CS_EVENT_PRI_LOW);
if (ret != 0) { if (ret == 0) {
s->state &= ~SOCKET_RESET_PENDING; send_event(skt, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW);
handle->event_callback_args.info = (void *)(u_long)ret; if (socket_reset(skt) == CS_SUCCESS)
EVENT(handle, CS_EVENT_RESET_COMPLETE, CS_EVENT_PRI_LOW); send_event(skt, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW);
} else { }
DEBUG(1, "cs: resetting socket %d\n", i);
send_event(s, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW); handle->event_callback_args.info = (void *)(u_long)ret;
s->reset_handle = handle; EVENT(handle, CS_EVENT_RESET_COMPLETE, CS_EVENT_PRI_LOW);
reset_socket(s);
} ret = CS_SUCCESS;
return CS_SUCCESS; } while (0);
up(&skt->skt_sem);
return ret;
} /* reset_card */ } /* reset_card */
/*====================================================================== /*======================================================================
...@@ -2057,42 +2141,56 @@ int pcmcia_reset_card(client_handle_t handle, client_req_t *req) ...@@ -2057,42 +2141,56 @@ int pcmcia_reset_card(client_handle_t handle, client_req_t *req)
int pcmcia_suspend_card(client_handle_t handle, client_req_t *req) int pcmcia_suspend_card(client_handle_t handle, client_req_t *req)
{ {
int i; socket_info_t *skt;
socket_info_t *s; int ret;
if (CHECK_HANDLE(handle)) if (CHECK_HANDLE(handle))
return CS_BAD_HANDLE; return CS_BAD_HANDLE;
i = handle->Socket; s = socket_table[i]; DEBUG(1, "cs: suspending socket %d\n", handle->Socket);
if (!(s->state & SOCKET_PRESENT)) skt = SOCKET(handle);
return CS_NO_CARD;
if (s->state & SOCKET_SUSPEND) down(&skt->skt_sem);
return CS_IN_USE; do {
if (!(skt->state & SOCKET_PRESENT)) {
DEBUG(1, "cs: suspending socket %d\n", i); ret = CS_NO_CARD;
send_event(s, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW); break;
suspend_socket(s); }
s->state |= SOCKET_SUSPEND; 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 */ } /* suspend_card */
int pcmcia_resume_card(client_handle_t handle, client_req_t *req) int pcmcia_resume_card(client_handle_t handle, client_req_t *req)
{ {
int i; socket_info_t *skt;
socket_info_t *s; int ret;
if (CHECK_HANDLE(handle)) if (CHECK_HANDLE(handle))
return CS_BAD_HANDLE; return CS_BAD_HANDLE;
i = handle->Socket; s = socket_table[i]; DEBUG(1, "cs: waking up socket %d\n", handle->Socket);
if (!(s->state & SOCKET_PRESENT)) skt = SOCKET(handle);
return CS_NO_CARD;
if (!(s->state & SOCKET_SUSPEND)) down(&skt->skt_sem);
return CS_IN_USE; do {
if (!(skt->state & SOCKET_PRESENT)) {
DEBUG(1, "cs: waking up socket %d\n", i); ret = CS_NO_CARD;
setup_socket(s); 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 */ } /* resume_card */
/*====================================================================== /*======================================================================
...@@ -2103,57 +2201,58 @@ int pcmcia_resume_card(client_handle_t handle, client_req_t *req) ...@@ -2103,57 +2201,58 @@ int pcmcia_resume_card(client_handle_t handle, client_req_t *req)
int pcmcia_eject_card(client_handle_t handle, client_req_t *req) int pcmcia_eject_card(client_handle_t handle, client_req_t *req)
{ {
int i, ret; socket_info_t *skt;
socket_info_t *s; int ret;
u_long flags;
if (CHECK_HANDLE(handle)) if (CHECK_HANDLE(handle))
return CS_BAD_HANDLE; return CS_BAD_HANDLE;
i = handle->Socket; s = socket_table[i]; DEBUG(1, "cs: user eject request on socket %d\n", handle->Socket);
if (!(s->state & SOCKET_PRESENT)) skt = SOCKET(handle);
return CS_NO_CARD;
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); socket_shutdown(skt);
if (ret != 0) ret = CS_SUCCESS;
return ret; } while (0);
up(&skt->skt_sem);
spin_lock_irqsave(&s->lock, flags); return ret;
do_shutdown(s);
spin_unlock_irqrestore(&s->lock, flags);
return CS_SUCCESS;
} /* eject_card */ } /* eject_card */
int pcmcia_insert_card(client_handle_t handle, client_req_t *req) int pcmcia_insert_card(client_handle_t handle, client_req_t *req)
{ {
int i, status; socket_info_t *skt;
socket_info_t *s; int ret;
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);
spin_lock_irqsave(&s->lock, flags); if (CHECK_HANDLE(handle))
if (!(s->state & SOCKET_SETUP_PENDING)) { return CS_BAD_HANDLE;
s->state |= SOCKET_SETUP_PENDING; DEBUG(1, "cs: user insert request on socket %d\n", handle->Socket);
spin_unlock_irqrestore(&s->lock, flags); skt = SOCKET(handle);
get_socket_status(s, &status);
if ((status & SS_DETECT) == 0 || (setup_socket(s) == 0)) { down(&skt->skt_sem);
s->state &= ~SOCKET_SETUP_PENDING; do {
return CS_NO_CARD; if (skt->state & SOCKET_PRESENT) {
} ret = CS_IN_USE;
} else break;
spin_unlock_irqrestore(&s->lock, flags); }
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 */ } /* insert_card */
/*====================================================================== /*======================================================================
......
...@@ -133,7 +133,6 @@ typedef struct socket_info_t { ...@@ -133,7 +133,6 @@ typedef struct socket_info_t {
u_short lock_count; u_short lock_count;
client_handle_t clients; client_handle_t clients;
u_int real_clients; u_int real_clients;
client_handle_t reset_handle;
pccard_mem_map cis_mem; pccard_mem_map cis_mem;
u_char *cis_virt; u_char *cis_virt;
config_t *config; config_t *config;
...@@ -155,6 +154,14 @@ typedef struct socket_info_t { ...@@ -155,6 +154,14 @@ typedef struct socket_info_t {
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
struct proc_dir_entry *proc; struct proc_dir_entry *proc;
#endif #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; } socket_info_t;
/* Flags in config state */ /* Flags in config state */
......
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