aboutsummaryrefslogtreecommitdiff
path: root/drivers/staging/comedi/comedi_fops.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/comedi/comedi_fops.c')
-rw-r--r--drivers/staging/comedi/comedi_fops.c545
1 files changed, 321 insertions, 224 deletions
diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c
index e336b281b84..00f2547024e 100644
--- a/drivers/staging/comedi/comedi_fops.c
+++ b/drivers/staging/comedi/comedi_fops.c
@@ -49,9 +49,13 @@
#include "comedi_internal.h"
+#define COMEDI_NUM_MINORS 0x100
+#define COMEDI_NUM_SUBDEVICE_MINORS \
+ (COMEDI_NUM_MINORS - COMEDI_NUM_BOARD_MINORS)
+
#ifdef CONFIG_COMEDI_DEBUG
int comedi_debug;
-EXPORT_SYMBOL(comedi_debug);
+EXPORT_SYMBOL_GPL(comedi_debug);
module_param(comedi_debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(comedi_debug,
"enable comedi core and driver debugging if non-zero (default 0)"
@@ -77,57 +81,151 @@ MODULE_PARM_DESC(comedi_default_buf_maxsize_kb,
"default maximum size of asynchronous buffer in KiB (default "
__MODULE_STRING(CONFIG_COMEDI_DEFAULT_BUF_MAXSIZE_KB) ")");
-struct comedi_file_info {
- struct comedi_device *device;
- struct comedi_subdevice *read_subdevice;
- struct comedi_subdevice *write_subdevice;
- struct device *hardware_device;
-};
+static DEFINE_MUTEX(comedi_board_minor_table_lock);
+static struct comedi_device
+*comedi_board_minor_table[COMEDI_NUM_BOARD_MINORS];
+
+static DEFINE_MUTEX(comedi_subdevice_minor_table_lock);
+/* Note: indexed by minor - COMEDI_NUM_BOARD_MINORS. */
+static struct comedi_subdevice
+*comedi_subdevice_minor_table[COMEDI_NUM_SUBDEVICE_MINORS];
+
+static struct class *comedi_class;
+static struct cdev comedi_cdev;
+
+static void comedi_device_init(struct comedi_device *dev)
+{
+ spin_lock_init(&dev->spinlock);
+ mutex_init(&dev->mutex);
+ dev->minor = -1;
+}
+
+static void comedi_device_cleanup(struct comedi_device *dev)
+{
+ struct module *driver_module = NULL;
+
+ if (dev == NULL)
+ return;
+ mutex_lock(&dev->mutex);
+ if (dev->attached)
+ driver_module = dev->driver->module;
+ comedi_device_detach(dev);
+ while (dev->use_count > 0) {
+ if (driver_module)
+ module_put(driver_module);
+ module_put(THIS_MODULE);
+ dev->use_count--;
+ }
+ mutex_unlock(&dev->mutex);
+ mutex_destroy(&dev->mutex);
+}
+
+static bool comedi_clear_board_dev(struct comedi_device *dev)
+{
+ unsigned int i = dev->minor;
+ bool cleared = false;
+
+ mutex_lock(&comedi_board_minor_table_lock);
+ if (dev == comedi_board_minor_table[i]) {
+ comedi_board_minor_table[i] = NULL;
+ cleared = true;
+ }
+ mutex_unlock(&comedi_board_minor_table_lock);
+ return cleared;
+}
+
+static struct comedi_device *comedi_clear_board_minor(unsigned minor)
+{
+ struct comedi_device *dev;
+
+ mutex_lock(&comedi_board_minor_table_lock);
+ dev = comedi_board_minor_table[minor];
+ comedi_board_minor_table[minor] = NULL;
+ mutex_unlock(&comedi_board_minor_table_lock);
+ return dev;
+}
+
+static void comedi_free_board_dev(struct comedi_device *dev)
+{
+ if (dev) {
+ if (dev->class_dev) {
+ device_destroy(comedi_class,
+ MKDEV(COMEDI_MAJOR, dev->minor));
+ }
+ comedi_device_cleanup(dev);
+ kfree(dev);
+ }
+}
-static DEFINE_SPINLOCK(comedi_file_info_table_lock);
-static struct comedi_file_info *comedi_file_info_table[COMEDI_NUM_MINORS];
+static struct comedi_subdevice
+*comedi_subdevice_from_minor(unsigned minor)
+{
+ struct comedi_subdevice *s;
+ unsigned int i = minor - COMEDI_NUM_BOARD_MINORS;
+
+ BUG_ON(i >= COMEDI_NUM_SUBDEVICE_MINORS);
+ mutex_lock(&comedi_subdevice_minor_table_lock);
+ s = comedi_subdevice_minor_table[i];
+ mutex_unlock(&comedi_subdevice_minor_table_lock);
+ return s;
+}
-static struct comedi_file_info *comedi_file_info_from_minor(unsigned minor)
+static struct comedi_device *comedi_dev_from_board_minor(unsigned minor)
{
- struct comedi_file_info *info;
+ struct comedi_device *dev;
- BUG_ON(minor >= COMEDI_NUM_MINORS);
- spin_lock(&comedi_file_info_table_lock);
- info = comedi_file_info_table[minor];
- spin_unlock(&comedi_file_info_table_lock);
- return info;
+ BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS);
+ mutex_lock(&comedi_board_minor_table_lock);
+ dev = comedi_board_minor_table[minor];
+ mutex_unlock(&comedi_board_minor_table_lock);
+ return dev;
}
-static struct comedi_device *
-comedi_dev_from_file_info(struct comedi_file_info *info)
+static struct comedi_device *comedi_dev_from_subdevice_minor(unsigned minor)
{
- return info ? info->device : NULL;
+ struct comedi_subdevice *s;
+
+ s = comedi_subdevice_from_minor(minor);
+ return s ? s->device : NULL;
}
struct comedi_device *comedi_dev_from_minor(unsigned minor)
{
- return comedi_dev_from_file_info(comedi_file_info_from_minor(minor));
+ if (minor < COMEDI_NUM_BOARD_MINORS)
+ return comedi_dev_from_board_minor(minor);
+ else
+ return comedi_dev_from_subdevice_minor(minor);
}
EXPORT_SYMBOL_GPL(comedi_dev_from_minor);
static struct comedi_subdevice *
-comedi_read_subdevice(const struct comedi_file_info *info)
+comedi_read_subdevice(const struct comedi_device *dev, unsigned int minor)
{
- if (info->read_subdevice)
- return info->read_subdevice;
- if (info->device)
- return info->device->read_subdev;
- return NULL;
+ struct comedi_subdevice *s;
+
+ if (minor >= COMEDI_NUM_BOARD_MINORS) {
+ s = comedi_subdevice_from_minor(minor);
+ if (!s || s->device != dev)
+ return NULL;
+ if (s->subdev_flags & SDF_CMD_READ)
+ return s;
+ }
+ return dev->read_subdev;
}
static struct comedi_subdevice *
-comedi_write_subdevice(const struct comedi_file_info *info)
+comedi_write_subdevice(const struct comedi_device *dev, unsigned int minor)
{
- if (info->write_subdevice)
- return info->write_subdevice;
- if (info->device)
- return info->device->write_subdev;
- return NULL;
+ struct comedi_subdevice *s;
+
+ if (minor >= COMEDI_NUM_BOARD_MINORS) {
+ s = comedi_subdevice_from_minor(minor);
+ if (!s || s->device != dev)
+ return NULL;
+ if (s->subdev_flags & SDF_CMD_WRITE)
+ return s;
+ }
+ return dev->write_subdev;
}
static int resize_async_buffer(struct comedi_device *dev,
@@ -172,27 +270,34 @@ static int resize_async_buffer(struct comedi_device *dev,
/* sysfs attribute files */
-static ssize_t show_max_read_buffer_kb(struct device *dev,
+static ssize_t show_max_read_buffer_kb(struct device *csdev,
struct device_attribute *attr, char *buf)
{
- struct comedi_file_info *info = dev_get_drvdata(dev);
- struct comedi_subdevice *s = comedi_read_subdevice(info);
+ unsigned int minor = MINOR(csdev->devt);
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
unsigned int size = 0;
- mutex_lock(&info->device->mutex);
+ dev = comedi_dev_from_minor(minor);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->mutex);
+ s = comedi_read_subdevice(dev, minor);
if (s && (s->subdev_flags & SDF_CMD_READ) && s->async)
size = s->async->max_bufsize / 1024;
- mutex_unlock(&info->device->mutex);
+ mutex_unlock(&dev->mutex);
return snprintf(buf, PAGE_SIZE, "%i\n", size);
}
-static ssize_t store_max_read_buffer_kb(struct device *dev,
+static ssize_t store_max_read_buffer_kb(struct device *csdev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct comedi_file_info *info = dev_get_drvdata(dev);
- struct comedi_subdevice *s = comedi_read_subdevice(info);
+ unsigned int minor = MINOR(csdev->devt);
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
unsigned int size;
int err;
@@ -203,37 +308,49 @@ static ssize_t store_max_read_buffer_kb(struct device *dev,
return -EINVAL;
size *= 1024;
- mutex_lock(&info->device->mutex);
+ dev = comedi_dev_from_minor(minor);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->mutex);
+ s = comedi_read_subdevice(dev, minor);
if (s && (s->subdev_flags & SDF_CMD_READ) && s->async)
s->async->max_bufsize = size;
else
err = -EINVAL;
- mutex_unlock(&info->device->mutex);
+ mutex_unlock(&dev->mutex);
return err ? err : count;
}
-static ssize_t show_read_buffer_kb(struct device *dev,
+static ssize_t show_read_buffer_kb(struct device *csdev,
struct device_attribute *attr, char *buf)
{
- struct comedi_file_info *info = dev_get_drvdata(dev);
- struct comedi_subdevice *s = comedi_read_subdevice(info);
+ unsigned int minor = MINOR(csdev->devt);
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
unsigned int size = 0;
- mutex_lock(&info->device->mutex);
+ dev = comedi_dev_from_minor(minor);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->mutex);
+ s = comedi_read_subdevice(dev, minor);
if (s && (s->subdev_flags & SDF_CMD_READ) && s->async)
size = s->async->prealloc_bufsz / 1024;
- mutex_unlock(&info->device->mutex);
+ mutex_unlock(&dev->mutex);
return snprintf(buf, PAGE_SIZE, "%i\n", size);
}
-static ssize_t store_read_buffer_kb(struct device *dev,
+static ssize_t store_read_buffer_kb(struct device *csdev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct comedi_file_info *info = dev_get_drvdata(dev);
- struct comedi_subdevice *s = comedi_read_subdevice(info);
+ unsigned int minor = MINOR(csdev->devt);
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
unsigned int size;
int err;
@@ -244,38 +361,50 @@ static ssize_t store_read_buffer_kb(struct device *dev,
return -EINVAL;
size *= 1024;
- mutex_lock(&info->device->mutex);
+ dev = comedi_dev_from_minor(minor);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->mutex);
+ s = comedi_read_subdevice(dev, minor);
if (s && (s->subdev_flags & SDF_CMD_READ) && s->async)
- err = resize_async_buffer(info->device, s, s->async, size);
+ err = resize_async_buffer(dev, s, s->async, size);
else
err = -EINVAL;
- mutex_unlock(&info->device->mutex);
+ mutex_unlock(&dev->mutex);
return err ? err : count;
}
-static ssize_t show_max_write_buffer_kb(struct device *dev,
+static ssize_t show_max_write_buffer_kb(struct device *csdev,
struct device_attribute *attr,
char *buf)
{
- struct comedi_file_info *info = dev_get_drvdata(dev);
- struct comedi_subdevice *s = comedi_write_subdevice(info);
+ unsigned int minor = MINOR(csdev->devt);
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
unsigned int size = 0;
- mutex_lock(&info->device->mutex);
+ dev = comedi_dev_from_minor(minor);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->mutex);
+ s = comedi_write_subdevice(dev, minor);
if (s && (s->subdev_flags & SDF_CMD_WRITE) && s->async)
size = s->async->max_bufsize / 1024;
- mutex_unlock(&info->device->mutex);
+ mutex_unlock(&dev->mutex);
return snprintf(buf, PAGE_SIZE, "%i\n", size);
}
-static ssize_t store_max_write_buffer_kb(struct device *dev,
+static ssize_t store_max_write_buffer_kb(struct device *csdev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct comedi_file_info *info = dev_get_drvdata(dev);
- struct comedi_subdevice *s = comedi_write_subdevice(info);
+ unsigned int minor = MINOR(csdev->devt);
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
unsigned int size;
int err;
@@ -286,37 +415,49 @@ static ssize_t store_max_write_buffer_kb(struct device *dev,
return -EINVAL;
size *= 1024;
- mutex_lock(&info->device->mutex);
+ dev = comedi_dev_from_minor(minor);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->mutex);
+ s = comedi_write_subdevice(dev, minor);
if (s && (s->subdev_flags & SDF_CMD_WRITE) && s->async)
s->async->max_bufsize = size;
else
err = -EINVAL;
- mutex_unlock(&info->device->mutex);
+ mutex_unlock(&dev->mutex);
return err ? err : count;
}
-static ssize_t show_write_buffer_kb(struct device *dev,
+static ssize_t show_write_buffer_kb(struct device *csdev,
struct device_attribute *attr, char *buf)
{
- struct comedi_file_info *info = dev_get_drvdata(dev);
- struct comedi_subdevice *s = comedi_write_subdevice(info);
+ unsigned int minor = MINOR(csdev->devt);
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
unsigned int size = 0;
- mutex_lock(&info->device->mutex);
+ dev = comedi_dev_from_minor(minor);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->mutex);
+ s = comedi_write_subdevice(dev, minor);
if (s && (s->subdev_flags & SDF_CMD_WRITE) && s->async)
size = s->async->prealloc_bufsz / 1024;
- mutex_unlock(&info->device->mutex);
+ mutex_unlock(&dev->mutex);
return snprintf(buf, PAGE_SIZE, "%i\n", size);
}
-static ssize_t store_write_buffer_kb(struct device *dev,
+static ssize_t store_write_buffer_kb(struct device *csdev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct comedi_file_info *info = dev_get_drvdata(dev);
- struct comedi_subdevice *s = comedi_write_subdevice(info);
+ unsigned int minor = MINOR(csdev->devt);
+ struct comedi_device *dev;
+ struct comedi_subdevice *s;
unsigned int size;
int err;
@@ -327,12 +468,17 @@ static ssize_t store_write_buffer_kb(struct device *dev,
return -EINVAL;
size *= 1024;
- mutex_lock(&info->device->mutex);
+ dev = comedi_dev_from_minor(minor);
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->mutex);
+ s = comedi_write_subdevice(dev, minor);
if (s && (s->subdev_flags & SDF_CMD_WRITE) && s->async)
- err = resize_async_buffer(info->device, s, s->async, size);
+ err = resize_async_buffer(dev, s, s->async, size);
else
err = -EINVAL;
- mutex_unlock(&info->device->mutex);
+ mutex_unlock(&dev->mutex);
return err ? err : count;
}
@@ -463,7 +609,6 @@ static int do_devconfig_ioctl(struct comedi_device *dev,
struct comedi_devconfig __user *arg)
{
struct comedi_devconfig it;
- int ret;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
@@ -490,15 +635,12 @@ static int do_devconfig_ioctl(struct comedi_device *dev,
return -EINVAL;
}
- ret = comedi_device_attach(dev, &it);
- if (ret == 0) {
- if (!try_module_get(dev->driver->module)) {
- comedi_device_detach(dev);
- ret = -ENOSYS;
- }
- }
+ if (dev->minor >= comedi_num_legacy_minors)
+ /* don't re-use dynamically allocated comedi devices */
+ return -EBUSY;
- return ret;
+ /* This increments the driver module count on success. */
+ return comedi_device_attach(dev, &it);
}
/*
@@ -581,7 +723,6 @@ static int do_devinfo_ioctl(struct comedi_device *dev,
struct file *file)
{
const unsigned minor = iminor(file_inode(file));
- struct comedi_file_info *info = comedi_file_info_from_minor(minor);
struct comedi_subdevice *s;
struct comedi_devinfo devinfo;
@@ -593,13 +734,13 @@ static int do_devinfo_ioctl(struct comedi_device *dev,
strlcpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN);
strlcpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN);
- s = comedi_read_subdevice(info);
+ s = comedi_read_subdevice(dev, minor);
if (s)
devinfo.read_subdevice = s->index;
else
devinfo.read_subdevice = -1;
- s = comedi_write_subdevice(info);
+ s = comedi_write_subdevice(dev, minor);
if (s)
devinfo.write_subdevice = s->index;
else
@@ -1616,8 +1757,7 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
const unsigned minor = iminor(file_inode(file));
- struct comedi_file_info *info = comedi_file_info_from_minor(minor);
- struct comedi_device *dev = comedi_dev_from_file_info(info);
+ struct comedi_device *dev = comedi_dev_from_minor(minor);
int rc;
if (!dev)
@@ -1635,9 +1775,18 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
}
rc = do_devconfig_ioctl(dev,
(struct comedi_devconfig __user *)arg);
- if (rc == 0)
- /* Evade comedi_auto_unconfig(). */
- info->hardware_device = NULL;
+ if (rc == 0) {
+ if (arg == 0 &&
+ dev->minor >= comedi_num_legacy_minors) {
+ /* Successfully unconfigured a dynamically
+ * allocated device. Try and remove it. */
+ if (comedi_clear_board_dev(dev)) {
+ mutex_unlock(&dev->mutex);
+ comedi_free_board_dev(dev);
+ return rc;
+ }
+ }
+ }
goto done;
}
@@ -1744,8 +1893,7 @@ static struct vm_operations_struct comedi_vm_ops = {
static int comedi_mmap(struct file *file, struct vm_area_struct *vma)
{
const unsigned minor = iminor(file_inode(file));
- struct comedi_file_info *info = comedi_file_info_from_minor(minor);
- struct comedi_device *dev = comedi_dev_from_file_info(info);
+ struct comedi_device *dev = comedi_dev_from_minor(minor);
struct comedi_subdevice *s;
struct comedi_async *async;
unsigned long start = vma->vm_start;
@@ -1766,9 +1914,9 @@ static int comedi_mmap(struct file *file, struct vm_area_struct *vma)
}
if (vma->vm_flags & VM_WRITE)
- s = comedi_write_subdevice(info);
+ s = comedi_write_subdevice(dev, minor);
else
- s = comedi_read_subdevice(info);
+ s = comedi_read_subdevice(dev, minor);
if (!s) {
retval = -EINVAL;
goto done;
@@ -1824,8 +1972,7 @@ static unsigned int comedi_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
const unsigned minor = iminor(file_inode(file));
- struct comedi_file_info *info = comedi_file_info_from_minor(minor);
- struct comedi_device *dev = comedi_dev_from_file_info(info);
+ struct comedi_device *dev = comedi_dev_from_minor(minor);
struct comedi_subdevice *s;
if (!dev)
@@ -1838,7 +1985,7 @@ static unsigned int comedi_poll(struct file *file, poll_table *wait)
goto done;
}
- s = comedi_read_subdevice(info);
+ s = comedi_read_subdevice(dev, minor);
if (s && s->async) {
poll_wait(file, &s->async->wait_head, wait);
if (!s->busy || !comedi_is_subdevice_running(s) ||
@@ -1846,7 +1993,7 @@ static unsigned int comedi_poll(struct file *file, poll_table *wait)
mask |= POLLIN | POLLRDNORM;
}
- s = comedi_write_subdevice(info);
+ s = comedi_write_subdevice(dev, minor);
if (s && s->async) {
unsigned int bps = bytes_per_sample(s->async->subdevice);
@@ -1870,8 +2017,7 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
int n, m, count = 0, retval = 0;
DECLARE_WAITQUEUE(wait, current);
const unsigned minor = iminor(file_inode(file));
- struct comedi_file_info *info = comedi_file_info_from_minor(minor);
- struct comedi_device *dev = comedi_dev_from_file_info(info);
+ struct comedi_device *dev = comedi_dev_from_minor(minor);
if (!dev)
return -ENODEV;
@@ -1881,7 +2027,7 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
return -ENODEV;
}
- s = comedi_write_subdevice(info);
+ s = comedi_write_subdevice(dev, minor);
if (!s || !s->async)
return -EIO;
@@ -1965,8 +2111,7 @@ static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes,
int n, m, count = 0, retval = 0;
DECLARE_WAITQUEUE(wait, current);
const unsigned minor = iminor(file_inode(file));
- struct comedi_file_info *info = comedi_file_info_from_minor(minor);
- struct comedi_device *dev = comedi_dev_from_file_info(info);
+ struct comedi_device *dev = comedi_dev_from_minor(minor);
if (!dev)
return -ENODEV;
@@ -1976,7 +2121,7 @@ static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes,
return -ENODEV;
}
- s = comedi_read_subdevice(info);
+ s = comedi_read_subdevice(dev, minor);
if (!s || !s->async)
return -EIO;
@@ -2067,12 +2212,12 @@ static int comedi_open(struct inode *inode, struct file *file)
/* This is slightly hacky, but we want module autoloading
* to work for root.
* case: user opens device, attached -> ok
- * case: user opens device, unattached, in_request_module=0 -> autoload
- * case: user opens device, unattached, in_request_module=1 -> fail
+ * case: user opens device, unattached, !in_request_module -> autoload
+ * case: user opens device, unattached, in_request_module -> fail
* case: root opens device, attached -> ok
- * case: root opens device, unattached, in_request_module=1 -> ok
+ * case: root opens device, unattached, in_request_module -> ok
* (typically called from modprobe)
- * case: root opens device, unattached, in_request_module=0 -> autoload
+ * case: root opens device, unattached, !in_request_module -> autoload
*
* The last could be changed to "-> ok", which would deny root
* autoloading.
@@ -2088,7 +2233,7 @@ static int comedi_open(struct inode *inode, struct file *file)
if (capable(CAP_NET_ADMIN) && dev->in_request_module)
goto ok;
- dev->in_request_module = 1;
+ dev->in_request_module = true;
#ifdef CONFIG_KMOD
mutex_unlock(&dev->mutex);
@@ -2096,7 +2241,7 @@ static int comedi_open(struct inode *inode, struct file *file)
mutex_lock(&dev->mutex);
#endif
- dev->in_request_module = 0;
+ dev->in_request_module = false;
if (!dev->attached && !capable(CAP_NET_ADMIN)) {
DPRINTK("not attached and not CAP_NET_ADMIN\n");
@@ -2195,14 +2340,11 @@ static const struct file_operations comedi_fops = {
.llseek = noop_llseek,
};
-static struct class *comedi_class;
-static struct cdev comedi_cdev;
-
void comedi_error(const struct comedi_device *dev, const char *s)
{
dev_err(dev->class_dev, "%s: %s\n", dev->driver->driver_name, s);
}
-EXPORT_SYMBOL(comedi_error);
+EXPORT_SYMBOL_GPL(comedi_error);
void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s)
{
@@ -2245,150 +2387,104 @@ void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s)
}
s->async->events = 0;
}
-EXPORT_SYMBOL(comedi_event);
-
-static void comedi_device_init(struct comedi_device *dev)
-{
- memset(dev, 0, sizeof(*dev));
- spin_lock_init(&dev->spinlock);
- mutex_init(&dev->mutex);
- dev->minor = -1;
-}
-
-static void comedi_device_cleanup(struct comedi_device *dev)
-{
- if (dev == NULL)
- return;
- mutex_lock(&dev->mutex);
- comedi_device_detach(dev);
- mutex_unlock(&dev->mutex);
- mutex_destroy(&dev->mutex);
-}
+EXPORT_SYMBOL_GPL(comedi_event);
-int comedi_alloc_board_minor(struct device *hardware_device)
+/* Note: the ->mutex is pre-locked on successful return */
+struct comedi_device *comedi_alloc_board_minor(struct device *hardware_device)
{
- struct comedi_file_info *info;
+ struct comedi_device *dev;
struct device *csdev;
unsigned i;
- info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (info == NULL)
- return -ENOMEM;
- info->device = kzalloc(sizeof(struct comedi_device), GFP_KERNEL);
- if (info->device == NULL) {
- kfree(info);
- return -ENOMEM;
- }
- info->hardware_device = hardware_device;
- comedi_device_init(info->device);
- spin_lock(&comedi_file_info_table_lock);
- for (i = 0; i < COMEDI_NUM_BOARD_MINORS; ++i) {
- if (comedi_file_info_table[i] == NULL) {
- comedi_file_info_table[i] = info;
+ dev = kzalloc(sizeof(struct comedi_device), GFP_KERNEL);
+ if (dev == NULL)
+ return ERR_PTR(-ENOMEM);
+ comedi_device_init(dev);
+ comedi_set_hw_dev(dev, hardware_device);
+ mutex_lock(&dev->mutex);
+ mutex_lock(&comedi_board_minor_table_lock);
+ for (i = hardware_device ? comedi_num_legacy_minors : 0;
+ i < COMEDI_NUM_BOARD_MINORS; ++i) {
+ if (comedi_board_minor_table[i] == NULL) {
+ comedi_board_minor_table[i] = dev;
break;
}
}
- spin_unlock(&comedi_file_info_table_lock);
+ mutex_unlock(&comedi_board_minor_table_lock);
if (i == COMEDI_NUM_BOARD_MINORS) {
- comedi_device_cleanup(info->device);
- kfree(info->device);
- kfree(info);
+ mutex_unlock(&dev->mutex);
+ comedi_device_cleanup(dev);
+ kfree(dev);
pr_err("comedi: error: ran out of minor numbers for board device files.\n");
- return -EBUSY;
+ return ERR_PTR(-EBUSY);
}
- info->device->minor = i;
+ dev->minor = i;
csdev = device_create(comedi_class, hardware_device,
MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i", i);
if (!IS_ERR(csdev))
- info->device->class_dev = csdev;
- dev_set_drvdata(csdev, info);
+ dev->class_dev = csdev;
- return i;
+ /* Note: dev->mutex needs to be unlocked by the caller. */
+ return dev;
}
-void comedi_free_board_minor(unsigned minor)
+static void comedi_free_board_minor(unsigned minor)
{
- struct comedi_file_info *info;
-
BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS);
- spin_lock(&comedi_file_info_table_lock);
- info = comedi_file_info_table[minor];
- comedi_file_info_table[minor] = NULL;
- spin_unlock(&comedi_file_info_table_lock);
-
- if (info) {
- struct comedi_device *dev = info->device;
- if (dev) {
- if (dev->class_dev) {
- device_destroy(comedi_class,
- MKDEV(COMEDI_MAJOR, dev->minor));
- }
- comedi_device_cleanup(dev);
- kfree(dev);
- }
- kfree(info);
- }
+ comedi_free_board_dev(comedi_clear_board_minor(minor));
}
-int comedi_find_board_minor(struct device *hardware_device)
+void comedi_release_hardware_device(struct device *hardware_device)
{
int minor;
- struct comedi_file_info *info;
-
- for (minor = 0; minor < COMEDI_NUM_BOARD_MINORS; minor++) {
- spin_lock(&comedi_file_info_table_lock);
- info = comedi_file_info_table[minor];
- if (info && info->hardware_device == hardware_device) {
- spin_unlock(&comedi_file_info_table_lock);
- return minor;
+ struct comedi_device *dev;
+
+ for (minor = comedi_num_legacy_minors; minor < COMEDI_NUM_BOARD_MINORS;
+ minor++) {
+ mutex_lock(&comedi_board_minor_table_lock);
+ dev = comedi_board_minor_table[minor];
+ if (dev && dev->hw_dev == hardware_device) {
+ comedi_board_minor_table[minor] = NULL;
+ mutex_unlock(&comedi_board_minor_table_lock);
+ comedi_free_board_dev(dev);
+ break;
}
- spin_unlock(&comedi_file_info_table_lock);
+ mutex_unlock(&comedi_board_minor_table_lock);
}
- return -ENODEV;
}
int comedi_alloc_subdevice_minor(struct comedi_subdevice *s)
{
struct comedi_device *dev = s->device;
- struct comedi_file_info *info;
struct device *csdev;
unsigned i;
- info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
- info->device = dev;
- if (s->subdev_flags & SDF_CMD_READ)
- info->read_subdevice = s;
- if (s->subdev_flags & SDF_CMD_WRITE)
- info->write_subdevice = s;
- spin_lock(&comedi_file_info_table_lock);
- for (i = COMEDI_FIRST_SUBDEVICE_MINOR; i < COMEDI_NUM_MINORS; ++i) {
- if (comedi_file_info_table[i] == NULL) {
- comedi_file_info_table[i] = info;
+ mutex_lock(&comedi_subdevice_minor_table_lock);
+ for (i = 0; i < COMEDI_NUM_SUBDEVICE_MINORS; ++i) {
+ if (comedi_subdevice_minor_table[i] == NULL) {
+ comedi_subdevice_minor_table[i] = s;
break;
}
}
- spin_unlock(&comedi_file_info_table_lock);
- if (i == COMEDI_NUM_MINORS) {
- kfree(info);
+ mutex_unlock(&comedi_subdevice_minor_table_lock);
+ if (i == COMEDI_NUM_SUBDEVICE_MINORS) {
pr_err("comedi: error: ran out of minor numbers for subdevice files.\n");
return -EBUSY;
}
+ i += COMEDI_NUM_BOARD_MINORS;
s->minor = i;
csdev = device_create(comedi_class, dev->class_dev,
MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i_subd%i",
dev->minor, s->index);
if (!IS_ERR(csdev))
s->class_dev = csdev;
- dev_set_drvdata(csdev, info);
return 0;
}
void comedi_free_subdevice_minor(struct comedi_subdevice *s)
{
- struct comedi_file_info *info;
+ unsigned int i;
if (s == NULL)
return;
@@ -2396,18 +2492,17 @@ void comedi_free_subdevice_minor(struct comedi_subdevice *s)
return;
BUG_ON(s->minor >= COMEDI_NUM_MINORS);
- BUG_ON(s->minor < COMEDI_FIRST_SUBDEVICE_MINOR);
-
- spin_lock(&comedi_file_info_table_lock);
- info = comedi_file_info_table[s->minor];
- comedi_file_info_table[s->minor] = NULL;
- spin_unlock(&comedi_file_info_table_lock);
+ BUG_ON(s->minor < COMEDI_NUM_BOARD_MINORS);
+ i = s->minor - COMEDI_NUM_BOARD_MINORS;
+ mutex_lock(&comedi_subdevice_minor_table_lock);
+ if (s == comedi_subdevice_minor_table[i])
+ comedi_subdevice_minor_table[i] = NULL;
+ mutex_unlock(&comedi_subdevice_minor_table_lock);
if (s->class_dev) {
device_destroy(comedi_class, MKDEV(COMEDI_MAJOR, s->minor));
s->class_dev = NULL;
}
- kfree(info);
}
static void comedi_cleanup_board_minors(void)
@@ -2432,9 +2527,6 @@ static int __init comedi_init(void)
return -EINVAL;
}
- memset(comedi_file_info_table, 0,
- sizeof(struct comedi_file_info *) * COMEDI_NUM_MINORS);
-
retval = register_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
COMEDI_NUM_MINORS, "comedi");
if (retval)
@@ -2463,14 +2555,17 @@ static int __init comedi_init(void)
/* create devices files for legacy/manual use */
for (i = 0; i < comedi_num_legacy_minors; i++) {
- int minor;
- minor = comedi_alloc_board_minor(NULL);
- if (minor < 0) {
+ struct comedi_device *dev;
+ dev = comedi_alloc_board_minor(NULL);
+ if (IS_ERR(dev)) {
comedi_cleanup_board_minors();
cdev_del(&comedi_cdev);
unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
COMEDI_NUM_MINORS);
- return minor;
+ return PTR_ERR(dev);
+ } else {
+ /* comedi_alloc_board_minor() locked the mutex */
+ mutex_unlock(&dev->mutex);
}
}
@@ -2483,8 +2578,10 @@ static void __exit comedi_cleanup(void)
int i;
comedi_cleanup_board_minors();
- for (i = 0; i < COMEDI_NUM_MINORS; ++i)
- BUG_ON(comedi_file_info_table[i]);
+ for (i = 0; i < COMEDI_NUM_BOARD_MINORS; ++i)
+ BUG_ON(comedi_board_minor_table[i]);
+ for (i = 0; i < COMEDI_NUM_SUBDEVICE_MINORS; ++i)
+ BUG_ON(comedi_subdevice_minor_table[i]);
class_destroy(comedi_class);
cdev_del(&comedi_cdev);