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,
======================================================================*/
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 class_device *dev);
#define to_class_data(dev) dev->class_data
......@@ -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);
socket_info_t *s_info;
unsigned int i, j;
unsigned int i, j, ret;
if (!cls_d)
return -EINVAL;
......@@ -330,6 +327,7 @@ int pcmcia_register_socket(struct class_device *class_dev)
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 @@ int pcmcia_register_socket(struct class_device *class_dev)
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 @@ int pcmcia_register_socket(struct class_device *class_dev)
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(class_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 @@ int pcmcia_register_socket(struct class_device *class_dev)
}
#endif
}
return 0;
return ret;
} /* pcmcia_register_socket */
......@@ -407,8 +419,12 @@ void pcmcia_unregister_socket(struct class_device *class_dev)
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 void free_regions(memory_handle_t *list)
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 @@ static void shutdown_socket(socket_info_t *s)
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 @@ static int send_event(socket_info_t *s, event_t event, int priority)
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);
}
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;
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;
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));
}
}
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");
}
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;
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 @@ 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)) {
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);
}
......@@ -1461,15 +1538,8 @@ int pcmcia_register_client(client_handle_t *handle, client_reg_t *req)
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;
......@@ -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 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 */
/*======================================================================
......@@ -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 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 */
/*======================================================================
......@@ -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 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 */
/*======================================================================
......
......@@ -133,7 +133,6 @@ typedef struct socket_info_t {
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 @@ typedef struct socket_info_t {
#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 */
......
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