#include <linux/list.h>
#include <linux/workqueue.h>
+#include <asm/atomic.h>
+
#include <pcmcia/version.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
int event_head, event_tail;
event_t event[MAX_EVENTS];
struct user_info_t *next;
+ struct pcmcia_bus_socket *socket;
} user_info_t;
/* Socket state information */
struct pcmcia_bus_socket {
+ atomic_t refcount;
client_handle_t handle;
int state;
user_info_t *user;
#define SOCKET_PRESENT 0x01
#define SOCKET_BUSY 0x02
#define SOCKET_REMOVAL_PENDING 0x10
+#define SOCKET_DEAD 0x80
/*====================================================================*/
static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info);
static struct pcmcia_bus_socket * get_socket_info_by_nr(unsigned int nr);
+static void pcmcia_put_bus_socket(struct pcmcia_bus_socket *s)
+{
+ if (atomic_dec_and_test(&s->refcount))
+ kfree(s);
+}
+
+static struct pcmcia_bus_socket *pcmcia_get_bus_socket(int nr)
+{
+ struct pcmcia_bus_socket *s;
+
+ s = get_socket_info_by_nr(nr);
+ if (s) {
+ WARN_ON(atomic_read(&s->refcount) == 0);
+ atomic_inc(&s->refcount);
+ }
+ return s;
+}
+
/**
* pcmcia_register_driver - register a PCMCIA driver with the bus core
*
DEBUG(0, "ds_open(socket %d)\n", i);
- s = get_socket_info_by_nr(i);
+ s = pcmcia_get_bus_socket(i);
if (!s)
return -ENODEV;
user->event_tail = user->event_head = 0;
user->next = s->user;
user->user_magic = USER_MAGIC;
+ user->socket = s;
s->user = user;
file->private_data = user;
DEBUG(0, "ds_release(socket %d)\n", i);
- s = get_socket_info_by_nr(i);
- if (!s)
- return 0;
-
user = file->private_data;
if (CHECK_USER(user))
goto out;
+ s = user->socket;
+
/* Unlink user data structure */
if ((file->f_flags & O_ACCMODE) != O_RDONLY)
s->state &= ~SOCKET_BUSY;
*link = user->next;
user->user_magic = 0;
kfree(user);
+ pcmcia_put_bus_socket(s);
out:
return 0;
} /* ds_release */
if (count < 4)
return -EINVAL;
- s = get_socket_info_by_nr(i);
- if (!s)
- return -ENODEV;
-
user = file->private_data;
if (CHECK_USER(user))
return -EIO;
+ s = user->socket;
+ if (s->state & SOCKET_DEAD)
+ return -EIO;
+
if (queue_empty(user)) {
interruptible_sleep_on(&s->queue);
if (signal_pending(current))
if ((file->f_flags & O_ACCMODE) == O_RDONLY)
return -EBADF;
- s = get_socket_info_by_nr(i);
- if (!s)
- return -ENODEV;
-
user = file->private_data;
if (CHECK_USER(user))
return -EIO;
+ s = user->socket;
+ if (s->state & SOCKET_DEAD)
+ return -EIO;
+
if (s->req_pending) {
s->req_pending--;
get_user(s->req_result, (int *)buf);
DEBUG(2, "ds_poll(socket %d)\n", i);
- s = get_socket_info_by_nr(i);
- if (!s)
- return POLLERR;
-
user = file->private_data;
if (CHECK_USER(user))
return POLLERR;
+ s = user->socket;
+ /*
+ * We don't check for a dead socket here since that
+ * will send cardmgr into an endless spin.
+ */
poll_wait(file, &s->queue, wait);
if (!queue_empty(user))
return POLLIN | POLLRDNORM;
u_int size;
int ret, err;
ds_ioctl_arg_t buf;
+ user_info_t *user;
DEBUG(2, "ds_ioctl(socket %d, %#x, %#lx)\n", i, cmd, arg);
- s = get_socket_info_by_nr(i);
- if (!s)
- return -ENODEV;
+ user = file->private_data;
+ if (CHECK_USER(user))
+ return -EIO;
+
+ s = user->socket;
+ if (s->state & SOCKET_DEAD)
+ return -EIO;
size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL;
if(!s)
return -ENOMEM;
memset(s, 0, sizeof(struct pcmcia_bus_socket));
+ atomic_set(&s->refcount, 1);
/*
* Ugly. But we want to wait for the socket threads to have started up.
pcmcia_deregister_client(socket->pcmcia->handle);
- kfree(socket->pcmcia);
+ socket->pcmcia->state |= SOCKET_DEAD;
+ pcmcia_put_bus_socket(socket->pcmcia);
socket->pcmcia = NULL;
return;