|
@@ -58,14 +58,35 @@ MODULE_LICENSE("GPL");
|
|
|
#ifdef CONFIG_COMEDI_DEBUG
|
|
|
int comedi_debug;
|
|
|
EXPORT_SYMBOL(comedi_debug);
|
|
|
-module_param(comedi_debug, int, 0644);
|
|
|
+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)"
|
|
|
+ );
|
|
|
#endif
|
|
|
|
|
|
bool comedi_autoconfig = 1;
|
|
|
-module_param(comedi_autoconfig, bool, 0444);
|
|
|
+module_param(comedi_autoconfig, bool, S_IRUGO);
|
|
|
+MODULE_PARM_DESC(comedi_autoconfig,
|
|
|
+ "enable drivers to auto-configure comedi devices (default 1)");
|
|
|
|
|
|
static int comedi_num_legacy_minors;
|
|
|
-module_param(comedi_num_legacy_minors, int, 0444);
|
|
|
+module_param(comedi_num_legacy_minors, int, S_IRUGO);
|
|
|
+MODULE_PARM_DESC(comedi_num_legacy_minors,
|
|
|
+ "number of comedi minor devices to reserve for non-auto-configured devices (default 0)"
|
|
|
+ );
|
|
|
+
|
|
|
+unsigned int comedi_default_buf_size_kb = CONFIG_COMEDI_DEFAULT_BUF_SIZE_KB;
|
|
|
+module_param(comedi_default_buf_size_kb, uint, S_IRUGO | S_IWUSR);
|
|
|
+MODULE_PARM_DESC(comedi_default_buf_size_kb,
|
|
|
+ "default asynchronous buffer size in KiB (default "
|
|
|
+ __MODULE_STRING(CONFIG_COMEDI_DEFAULT_BUF_SIZE_KB) ")");
|
|
|
+
|
|
|
+unsigned int comedi_default_buf_maxsize_kb
|
|
|
+ = CONFIG_COMEDI_DEFAULT_BUF_MAXSIZE_KB;
|
|
|
+module_param(comedi_default_buf_maxsize_kb, uint, S_IRUGO | S_IWUSR);
|
|
|
+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) ")");
|
|
|
|
|
|
static DEFINE_SPINLOCK(comedi_file_info_table_lock);
|
|
|
static struct comedi_device_file_info
|
|
@@ -108,15 +129,283 @@ static int do_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
|
|
|
static int comedi_fasync(int fd, struct file *file, int on);
|
|
|
|
|
|
static int is_device_busy(struct comedi_device *dev);
|
|
|
+
|
|
|
static int resize_async_buffer(struct comedi_device *dev,
|
|
|
struct comedi_subdevice *s,
|
|
|
- struct comedi_async *async, unsigned new_size);
|
|
|
+ struct comedi_async *async, unsigned new_size)
|
|
|
+{
|
|
|
+ int retval;
|
|
|
+
|
|
|
+ if (new_size > async->max_bufsize)
|
|
|
+ return -EPERM;
|
|
|
+
|
|
|
+ if (s->busy) {
|
|
|
+ DPRINTK("subdevice is busy, cannot resize buffer\n");
|
|
|
+ return -EBUSY;
|
|
|
+ }
|
|
|
+ if (async->mmap_count) {
|
|
|
+ DPRINTK("subdevice is mmapped, cannot resize buffer\n");
|
|
|
+ return -EBUSY;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!async->prealloc_buf)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /* make sure buffer is an integral number of pages
|
|
|
+ * (we round up) */
|
|
|
+ new_size = (new_size + PAGE_SIZE - 1) & PAGE_MASK;
|
|
|
+
|
|
|
+ retval = comedi_buf_alloc(dev, s, new_size);
|
|
|
+ if (retval < 0)
|
|
|
+ return retval;
|
|
|
+
|
|
|
+ if (s->buf_change) {
|
|
|
+ retval = s->buf_change(dev, s, new_size);
|
|
|
+ if (retval < 0)
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+
|
|
|
+ DPRINTK("comedi%i subd %d buffer resized to %i bytes\n",
|
|
|
+ dev->minor, (int)(s - dev->subdevices), async->prealloc_bufsz);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* sysfs attribute files */
|
|
|
+
|
|
|
+static const unsigned bytes_per_kibi = 1024;
|
|
|
+
|
|
|
+static ssize_t show_max_read_buffer_kb(struct device *dev,
|
|
|
+ struct device_attribute *attr, char *buf)
|
|
|
+{
|
|
|
+ ssize_t retval;
|
|
|
+ struct comedi_device_file_info *info = dev_get_drvdata(dev);
|
|
|
+ unsigned max_buffer_size_kb = 0;
|
|
|
+ struct comedi_subdevice *const read_subdevice =
|
|
|
+ comedi_get_read_subdevice(info);
|
|
|
+
|
|
|
+ mutex_lock(&info->device->mutex);
|
|
|
+ if (read_subdevice &&
|
|
|
+ (read_subdevice->subdev_flags & SDF_CMD_READ) &&
|
|
|
+ read_subdevice->async) {
|
|
|
+ max_buffer_size_kb = read_subdevice->async->max_bufsize /
|
|
|
+ bytes_per_kibi;
|
|
|
+ }
|
|
|
+ retval = snprintf(buf, PAGE_SIZE, "%i\n", max_buffer_size_kb);
|
|
|
+ mutex_unlock(&info->device->mutex);
|
|
|
+
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t store_max_read_buffer_kb(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ const char *buf, size_t count)
|
|
|
+{
|
|
|
+ struct comedi_device_file_info *info = dev_get_drvdata(dev);
|
|
|
+ unsigned int new_max_size_kb;
|
|
|
+ unsigned int new_max_size;
|
|
|
+ int ret;
|
|
|
+ struct comedi_subdevice *const read_subdevice =
|
|
|
+ comedi_get_read_subdevice(info);
|
|
|
+
|
|
|
+ ret = kstrtouint(buf, 10, &new_max_size_kb);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ if (new_max_size_kb > (UINT_MAX / bytes_per_kibi))
|
|
|
+ return -EINVAL;
|
|
|
+ new_max_size = new_max_size_kb * bytes_per_kibi;
|
|
|
+
|
|
|
+ mutex_lock(&info->device->mutex);
|
|
|
+ if (read_subdevice == NULL ||
|
|
|
+ (read_subdevice->subdev_flags & SDF_CMD_READ) == 0 ||
|
|
|
+ read_subdevice->async == NULL) {
|
|
|
+ mutex_unlock(&info->device->mutex);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ read_subdevice->async->max_bufsize = new_max_size;
|
|
|
+ mutex_unlock(&info->device->mutex);
|
|
|
+
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t show_read_buffer_kb(struct device *dev,
|
|
|
+ struct device_attribute *attr, char *buf)
|
|
|
+{
|
|
|
+ ssize_t retval;
|
|
|
+ struct comedi_device_file_info *info = dev_get_drvdata(dev);
|
|
|
+ unsigned buffer_size_kb = 0;
|
|
|
+ struct comedi_subdevice *const read_subdevice =
|
|
|
+ comedi_get_read_subdevice(info);
|
|
|
+
|
|
|
+ mutex_lock(&info->device->mutex);
|
|
|
+ if (read_subdevice &&
|
|
|
+ (read_subdevice->subdev_flags & SDF_CMD_READ) &&
|
|
|
+ read_subdevice->async) {
|
|
|
+ buffer_size_kb = read_subdevice->async->prealloc_bufsz /
|
|
|
+ bytes_per_kibi;
|
|
|
+ }
|
|
|
+ retval = snprintf(buf, PAGE_SIZE, "%i\n", buffer_size_kb);
|
|
|
+ mutex_unlock(&info->device->mutex);
|
|
|
+
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t store_read_buffer_kb(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ const char *buf, size_t count)
|
|
|
+{
|
|
|
+ struct comedi_device_file_info *info = dev_get_drvdata(dev);
|
|
|
+ unsigned int new_size_kb;
|
|
|
+ unsigned int new_size;
|
|
|
+ int retval;
|
|
|
+ int ret;
|
|
|
+ struct comedi_subdevice *const read_subdevice =
|
|
|
+ comedi_get_read_subdevice(info);
|
|
|
+
|
|
|
+ ret = kstrtouint(buf, 10, &new_size_kb);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ if (new_size_kb > (UINT_MAX / bytes_per_kibi))
|
|
|
+ return -EINVAL;
|
|
|
+ new_size = new_size_kb * bytes_per_kibi;
|
|
|
+
|
|
|
+ mutex_lock(&info->device->mutex);
|
|
|
+ if (read_subdevice == NULL ||
|
|
|
+ (read_subdevice->subdev_flags & SDF_CMD_READ) == 0 ||
|
|
|
+ read_subdevice->async == NULL) {
|
|
|
+ mutex_unlock(&info->device->mutex);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ retval = resize_async_buffer(info->device, read_subdevice,
|
|
|
+ read_subdevice->async, new_size);
|
|
|
+ mutex_unlock(&info->device->mutex);
|
|
|
+
|
|
|
+ if (retval < 0)
|
|
|
+ return retval;
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t show_max_write_buffer_kb(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ char *buf)
|
|
|
+{
|
|
|
+ ssize_t retval;
|
|
|
+ struct comedi_device_file_info *info = dev_get_drvdata(dev);
|
|
|
+ unsigned max_buffer_size_kb = 0;
|
|
|
+ struct comedi_subdevice *const write_subdevice =
|
|
|
+ comedi_get_write_subdevice(info);
|
|
|
+
|
|
|
+ mutex_lock(&info->device->mutex);
|
|
|
+ if (write_subdevice &&
|
|
|
+ (write_subdevice->subdev_flags & SDF_CMD_WRITE) &&
|
|
|
+ write_subdevice->async) {
|
|
|
+ max_buffer_size_kb = write_subdevice->async->max_bufsize /
|
|
|
+ bytes_per_kibi;
|
|
|
+ }
|
|
|
+ retval = snprintf(buf, PAGE_SIZE, "%i\n", max_buffer_size_kb);
|
|
|
+ mutex_unlock(&info->device->mutex);
|
|
|
+
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t store_max_write_buffer_kb(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ const char *buf, size_t count)
|
|
|
+{
|
|
|
+ struct comedi_device_file_info *info = dev_get_drvdata(dev);
|
|
|
+ unsigned int new_max_size_kb;
|
|
|
+ unsigned int new_max_size;
|
|
|
+ int ret;
|
|
|
+ struct comedi_subdevice *const write_subdevice =
|
|
|
+ comedi_get_write_subdevice(info);
|
|
|
+
|
|
|
+ ret = kstrtouint(buf, 10, &new_max_size_kb);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ if (new_max_size_kb > (UINT_MAX / bytes_per_kibi))
|
|
|
+ return -EINVAL;
|
|
|
+ new_max_size = new_max_size_kb * bytes_per_kibi;
|
|
|
+
|
|
|
+ mutex_lock(&info->device->mutex);
|
|
|
+ if (write_subdevice == NULL ||
|
|
|
+ (write_subdevice->subdev_flags & SDF_CMD_WRITE) == 0 ||
|
|
|
+ write_subdevice->async == NULL) {
|
|
|
+ mutex_unlock(&info->device->mutex);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ write_subdevice->async->max_bufsize = new_max_size;
|
|
|
+ mutex_unlock(&info->device->mutex);
|
|
|
+
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t show_write_buffer_kb(struct device *dev,
|
|
|
+ struct device_attribute *attr, char *buf)
|
|
|
+{
|
|
|
+ ssize_t retval;
|
|
|
+ struct comedi_device_file_info *info = dev_get_drvdata(dev);
|
|
|
+ unsigned buffer_size_kb = 0;
|
|
|
+ struct comedi_subdevice *const write_subdevice =
|
|
|
+ comedi_get_write_subdevice(info);
|
|
|
+
|
|
|
+ mutex_lock(&info->device->mutex);
|
|
|
+ if (write_subdevice &&
|
|
|
+ (write_subdevice->subdev_flags & SDF_CMD_WRITE) &&
|
|
|
+ write_subdevice->async) {
|
|
|
+ buffer_size_kb = write_subdevice->async->prealloc_bufsz /
|
|
|
+ bytes_per_kibi;
|
|
|
+ }
|
|
|
+ retval = snprintf(buf, PAGE_SIZE, "%i\n", buffer_size_kb);
|
|
|
+ mutex_unlock(&info->device->mutex);
|
|
|
+
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t store_write_buffer_kb(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ const char *buf, size_t count)
|
|
|
+{
|
|
|
+ struct comedi_device_file_info *info = dev_get_drvdata(dev);
|
|
|
+ unsigned int new_size_kb;
|
|
|
+ unsigned int new_size;
|
|
|
+ int retval;
|
|
|
+ int ret;
|
|
|
+ struct comedi_subdevice *const write_subdevice =
|
|
|
+ comedi_get_write_subdevice(info);
|
|
|
+
|
|
|
+ ret = kstrtouint(buf, 10, &new_size_kb);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ if (new_size_kb > (UINT_MAX / bytes_per_kibi))
|
|
|
+ return -EINVAL;
|
|
|
+ new_size = ((uint64_t) new_size_kb) * bytes_per_kibi;
|
|
|
|
|
|
-/* declarations for sysfs attribute files */
|
|
|
-static struct device_attribute dev_attr_max_read_buffer_kb;
|
|
|
-static struct device_attribute dev_attr_read_buffer_kb;
|
|
|
-static struct device_attribute dev_attr_max_write_buffer_kb;
|
|
|
-static struct device_attribute dev_attr_write_buffer_kb;
|
|
|
+ mutex_lock(&info->device->mutex);
|
|
|
+ if (write_subdevice == NULL ||
|
|
|
+ (write_subdevice->subdev_flags & SDF_CMD_WRITE) == 0 ||
|
|
|
+ write_subdevice->async == NULL) {
|
|
|
+ mutex_unlock(&info->device->mutex);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ retval = resize_async_buffer(info->device, write_subdevice,
|
|
|
+ write_subdevice->async, new_size);
|
|
|
+ mutex_unlock(&info->device->mutex);
|
|
|
+
|
|
|
+ if (retval < 0)
|
|
|
+ return retval;
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
+static struct device_attribute comedi_dev_attrs[] = {
|
|
|
+ __ATTR(max_read_buffer_kb, S_IRUGO | S_IWUSR,
|
|
|
+ show_max_read_buffer_kb, store_max_read_buffer_kb),
|
|
|
+ __ATTR(read_buffer_kb, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
|
+ show_read_buffer_kb, store_read_buffer_kb),
|
|
|
+ __ATTR(max_write_buffer_kb, S_IRUGO | S_IWUSR,
|
|
|
+ show_max_write_buffer_kb, store_max_write_buffer_kb),
|
|
|
+ __ATTR(write_buffer_kb, S_IRUGO | S_IWUSR | S_IWGRP,
|
|
|
+ show_write_buffer_kb, store_write_buffer_kb),
|
|
|
+ __ATTR_NULL
|
|
|
+};
|
|
|
|
|
|
static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
|
|
|
unsigned long arg)
|
|
@@ -280,7 +569,7 @@ static int do_devconfig_ioctl(struct comedi_device *dev,
|
|
|
if (ret == 0) {
|
|
|
if (!try_module_get(dev->driver->module)) {
|
|
|
comedi_device_detach(dev);
|
|
|
- return -ENOSYS;
|
|
|
+ ret = -ENOSYS;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1545,7 +1834,7 @@ done:
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
|
-static unsigned int comedi_poll(struct file *file, poll_table * wait)
|
|
|
+static unsigned int comedi_poll(struct file *file, poll_table *wait)
|
|
|
{
|
|
|
unsigned int mask = 0;
|
|
|
const unsigned minor = iminor(file->f_dentry->d_inode);
|
|
@@ -1880,827 +2169,473 @@ static int comedi_open(struct inode *inode, struct file *file)
|
|
|
|
|
|
dev->in_request_module = 1;
|
|
|
|
|
|
-#ifdef CONFIG_KMOD
|
|
|
- mutex_unlock(&dev->mutex);
|
|
|
- request_module("char-major-%i-%i", COMEDI_MAJOR, dev->minor);
|
|
|
- mutex_lock(&dev->mutex);
|
|
|
-#endif
|
|
|
-
|
|
|
- dev->in_request_module = 0;
|
|
|
-
|
|
|
- if (!dev->attached && !capable(CAP_NET_ADMIN)) {
|
|
|
- DPRINTK("not attached and not CAP_NET_ADMIN\n");
|
|
|
- mutex_unlock(&dev->mutex);
|
|
|
- return -ENODEV;
|
|
|
- }
|
|
|
-ok:
|
|
|
- __module_get(THIS_MODULE);
|
|
|
-
|
|
|
- if (dev->attached) {
|
|
|
- if (!try_module_get(dev->driver->module)) {
|
|
|
- module_put(THIS_MODULE);
|
|
|
- mutex_unlock(&dev->mutex);
|
|
|
- return -ENOSYS;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (dev->attached && dev->use_count == 0 && dev->open) {
|
|
|
- int rc = dev->open(dev);
|
|
|
- if (rc < 0) {
|
|
|
- module_put(dev->driver->module);
|
|
|
- module_put(THIS_MODULE);
|
|
|
- mutex_unlock(&dev->mutex);
|
|
|
- return rc;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- dev->use_count++;
|
|
|
-
|
|
|
- mutex_unlock(&dev->mutex);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static int comedi_close(struct inode *inode, struct file *file)
|
|
|
-{
|
|
|
- const unsigned minor = iminor(inode);
|
|
|
- struct comedi_subdevice *s = NULL;
|
|
|
- int i;
|
|
|
- struct comedi_device_file_info *dev_file_info;
|
|
|
- struct comedi_device *dev;
|
|
|
- dev_file_info = comedi_get_device_file_info(minor);
|
|
|
-
|
|
|
- if (dev_file_info == NULL)
|
|
|
- return -ENODEV;
|
|
|
- dev = dev_file_info->device;
|
|
|
- if (dev == NULL)
|
|
|
- return -ENODEV;
|
|
|
-
|
|
|
- mutex_lock(&dev->mutex);
|
|
|
-
|
|
|
- if (dev->subdevices) {
|
|
|
- for (i = 0; i < dev->n_subdevices; i++) {
|
|
|
- s = dev->subdevices + i;
|
|
|
-
|
|
|
- if (s->busy == file)
|
|
|
- do_cancel(dev, s);
|
|
|
- if (s->lock == file)
|
|
|
- s->lock = NULL;
|
|
|
- }
|
|
|
- }
|
|
|
- if (dev->attached && dev->use_count == 1 && dev->close)
|
|
|
- dev->close(dev);
|
|
|
-
|
|
|
- module_put(THIS_MODULE);
|
|
|
- if (dev->attached)
|
|
|
- module_put(dev->driver->module);
|
|
|
-
|
|
|
- dev->use_count--;
|
|
|
-
|
|
|
- mutex_unlock(&dev->mutex);
|
|
|
-
|
|
|
- if (file->f_flags & FASYNC)
|
|
|
- comedi_fasync(-1, file, 0);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static int comedi_fasync(int fd, struct file *file, int on)
|
|
|
-{
|
|
|
- const unsigned minor = iminor(file->f_dentry->d_inode);
|
|
|
- struct comedi_device_file_info *dev_file_info;
|
|
|
- struct comedi_device *dev;
|
|
|
- dev_file_info = comedi_get_device_file_info(minor);
|
|
|
-
|
|
|
- if (dev_file_info == NULL)
|
|
|
- return -ENODEV;
|
|
|
- dev = dev_file_info->device;
|
|
|
- if (dev == NULL)
|
|
|
- return -ENODEV;
|
|
|
-
|
|
|
- return fasync_helper(fd, file, on, &dev->async_queue);
|
|
|
-}
|
|
|
-
|
|
|
-const struct file_operations comedi_fops = {
|
|
|
- .owner = THIS_MODULE,
|
|
|
- .unlocked_ioctl = comedi_unlocked_ioctl,
|
|
|
- .compat_ioctl = comedi_compat_ioctl,
|
|
|
- .open = comedi_open,
|
|
|
- .release = comedi_close,
|
|
|
- .read = comedi_read,
|
|
|
- .write = comedi_write,
|
|
|
- .mmap = comedi_mmap,
|
|
|
- .poll = comedi_poll,
|
|
|
- .fasync = comedi_fasync,
|
|
|
- .llseek = noop_llseek,
|
|
|
-};
|
|
|
-
|
|
|
-struct class *comedi_class;
|
|
|
-static struct cdev comedi_cdev;
|
|
|
-
|
|
|
-static void comedi_cleanup_legacy_minors(void)
|
|
|
-{
|
|
|
- unsigned i;
|
|
|
-
|
|
|
- for (i = 0; i < comedi_num_legacy_minors; i++)
|
|
|
- comedi_free_board_minor(i);
|
|
|
-}
|
|
|
-
|
|
|
-static int __init comedi_init(void)
|
|
|
-{
|
|
|
- int i;
|
|
|
- int retval;
|
|
|
-
|
|
|
- printk(KERN_INFO "comedi: version " COMEDI_RELEASE
|
|
|
- " - http://www.comedi.org\n");
|
|
|
-
|
|
|
- if (comedi_num_legacy_minors < 0 ||
|
|
|
- comedi_num_legacy_minors > COMEDI_NUM_BOARD_MINORS) {
|
|
|
- printk(KERN_ERR "comedi: error: invalid value for module "
|
|
|
- "parameter \"comedi_num_legacy_minors\". Valid values "
|
|
|
- "are 0 through %i.\n", COMEDI_NUM_BOARD_MINORS);
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * comedi is unusable if both comedi_autoconfig and
|
|
|
- * comedi_num_legacy_minors are zero, so we might as well adjust the
|
|
|
- * defaults in that case
|
|
|
- */
|
|
|
- if (comedi_autoconfig == 0 && comedi_num_legacy_minors == 0)
|
|
|
- comedi_num_legacy_minors = 16;
|
|
|
-
|
|
|
- memset(comedi_file_info_table, 0,
|
|
|
- sizeof(struct comedi_device_file_info *) * COMEDI_NUM_MINORS);
|
|
|
-
|
|
|
- retval = register_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
|
|
|
- COMEDI_NUM_MINORS, "comedi");
|
|
|
- if (retval)
|
|
|
- return -EIO;
|
|
|
- cdev_init(&comedi_cdev, &comedi_fops);
|
|
|
- comedi_cdev.owner = THIS_MODULE;
|
|
|
- kobject_set_name(&comedi_cdev.kobj, "comedi");
|
|
|
- if (cdev_add(&comedi_cdev, MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS)) {
|
|
|
- unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
|
|
|
- COMEDI_NUM_MINORS);
|
|
|
- return -EIO;
|
|
|
- }
|
|
|
- comedi_class = class_create(THIS_MODULE, "comedi");
|
|
|
- if (IS_ERR(comedi_class)) {
|
|
|
- printk(KERN_ERR "comedi: failed to create class");
|
|
|
- cdev_del(&comedi_cdev);
|
|
|
- unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
|
|
|
- COMEDI_NUM_MINORS);
|
|
|
- return PTR_ERR(comedi_class);
|
|
|
- }
|
|
|
-
|
|
|
- /* XXX requires /proc interface */
|
|
|
- comedi_proc_init();
|
|
|
-
|
|
|
- /* 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) {
|
|
|
- comedi_cleanup_legacy_minors();
|
|
|
- cdev_del(&comedi_cdev);
|
|
|
- unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
|
|
|
- COMEDI_NUM_MINORS);
|
|
|
- return minor;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static void __exit comedi_cleanup(void)
|
|
|
-{
|
|
|
- int i;
|
|
|
-
|
|
|
- comedi_cleanup_legacy_minors();
|
|
|
- for (i = 0; i < COMEDI_NUM_MINORS; ++i)
|
|
|
- BUG_ON(comedi_file_info_table[i]);
|
|
|
-
|
|
|
- class_destroy(comedi_class);
|
|
|
- cdev_del(&comedi_cdev);
|
|
|
- unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS);
|
|
|
-
|
|
|
- comedi_proc_cleanup();
|
|
|
-}
|
|
|
-
|
|
|
-module_init(comedi_init);
|
|
|
-module_exit(comedi_cleanup);
|
|
|
-
|
|
|
-void comedi_error(const struct comedi_device *dev, const char *s)
|
|
|
-{
|
|
|
- printk(KERN_ERR "comedi%d: %s: %s\n", dev->minor,
|
|
|
- dev->driver->driver_name, s);
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(comedi_error);
|
|
|
-
|
|
|
-void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s)
|
|
|
-{
|
|
|
- struct comedi_async *async = s->async;
|
|
|
- unsigned runflags = 0;
|
|
|
- unsigned runflags_mask = 0;
|
|
|
-
|
|
|
- /* DPRINTK("comedi_event 0x%x\n",mask); */
|
|
|
+#ifdef CONFIG_KMOD
|
|
|
+ mutex_unlock(&dev->mutex);
|
|
|
+ request_module("char-major-%i-%i", COMEDI_MAJOR, dev->minor);
|
|
|
+ mutex_lock(&dev->mutex);
|
|
|
+#endif
|
|
|
|
|
|
- if ((comedi_get_subdevice_runflags(s) & SRF_RUNNING) == 0)
|
|
|
- return;
|
|
|
+ dev->in_request_module = 0;
|
|
|
|
|
|
- if (s->
|
|
|
- async->events & (COMEDI_CB_EOA | COMEDI_CB_ERROR |
|
|
|
- COMEDI_CB_OVERFLOW)) {
|
|
|
- runflags_mask |= SRF_RUNNING;
|
|
|
- }
|
|
|
- /* remember if an error event has occurred, so an error
|
|
|
- * can be returned the next time the user does a read() */
|
|
|
- if (s->async->events & (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) {
|
|
|
- runflags_mask |= SRF_ERROR;
|
|
|
- runflags |= SRF_ERROR;
|
|
|
- }
|
|
|
- if (runflags_mask) {
|
|
|
- /*sets SRF_ERROR and SRF_RUNNING together atomically */
|
|
|
- comedi_set_subdevice_runflags(s, runflags_mask, runflags);
|
|
|
+ if (!dev->attached && !capable(CAP_NET_ADMIN)) {
|
|
|
+ DPRINTK("not attached and not CAP_NET_ADMIN\n");
|
|
|
+ mutex_unlock(&dev->mutex);
|
|
|
+ return -ENODEV;
|
|
|
}
|
|
|
+ok:
|
|
|
+ __module_get(THIS_MODULE);
|
|
|
|
|
|
- if (async->cb_mask & s->async->events) {
|
|
|
- if (comedi_get_subdevice_runflags(s) & SRF_USER) {
|
|
|
- wake_up_interruptible(&async->wait_head);
|
|
|
- if (s->subdev_flags & SDF_CMD_READ)
|
|
|
- kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
|
|
|
- if (s->subdev_flags & SDF_CMD_WRITE)
|
|
|
- kill_fasync(&dev->async_queue, SIGIO, POLL_OUT);
|
|
|
- } else {
|
|
|
- if (async->cb_func)
|
|
|
- async->cb_func(s->async->events, async->cb_arg);
|
|
|
+ if (dev->attached) {
|
|
|
+ if (!try_module_get(dev->driver->module)) {
|
|
|
+ module_put(THIS_MODULE);
|
|
|
+ mutex_unlock(&dev->mutex);
|
|
|
+ return -ENOSYS;
|
|
|
}
|
|
|
}
|
|
|
- s->async->events = 0;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(comedi_event);
|
|
|
-
|
|
|
-unsigned comedi_get_subdevice_runflags(struct comedi_subdevice *s)
|
|
|
-{
|
|
|
- unsigned long flags;
|
|
|
- unsigned runflags;
|
|
|
-
|
|
|
- spin_lock_irqsave(&s->spin_lock, flags);
|
|
|
- runflags = s->runflags;
|
|
|
- spin_unlock_irqrestore(&s->spin_lock, flags);
|
|
|
- return runflags;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(comedi_get_subdevice_runflags);
|
|
|
|
|
|
-static int is_device_busy(struct comedi_device *dev)
|
|
|
-{
|
|
|
- struct comedi_subdevice *s;
|
|
|
- int i;
|
|
|
+ if (dev->attached && dev->use_count == 0 && dev->open) {
|
|
|
+ int rc = dev->open(dev);
|
|
|
+ if (rc < 0) {
|
|
|
+ module_put(dev->driver->module);
|
|
|
+ module_put(THIS_MODULE);
|
|
|
+ mutex_unlock(&dev->mutex);
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- if (!dev->attached)
|
|
|
- return 0;
|
|
|
+ dev->use_count++;
|
|
|
|
|
|
- for (i = 0; i < dev->n_subdevices; i++) {
|
|
|
- s = dev->subdevices + i;
|
|
|
- if (s->busy)
|
|
|
- return 1;
|
|
|
- if (s->async && s->async->mmap_count)
|
|
|
- return 1;
|
|
|
- }
|
|
|
+ mutex_unlock(&dev->mutex);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static void comedi_device_init(struct comedi_device *dev)
|
|
|
+static int comedi_close(struct inode *inode, struct file *file)
|
|
|
{
|
|
|
- memset(dev, 0, sizeof(struct comedi_device));
|
|
|
- spin_lock_init(&dev->spinlock);
|
|
|
- mutex_init(&dev->mutex);
|
|
|
- dev->minor = -1;
|
|
|
-}
|
|
|
+ const unsigned minor = iminor(inode);
|
|
|
+ struct comedi_subdevice *s = NULL;
|
|
|
+ int i;
|
|
|
+ struct comedi_device_file_info *dev_file_info;
|
|
|
+ struct comedi_device *dev;
|
|
|
+ dev_file_info = comedi_get_device_file_info(minor);
|
|
|
|
|
|
-static void comedi_device_cleanup(struct comedi_device *dev)
|
|
|
-{
|
|
|
+ if (dev_file_info == NULL)
|
|
|
+ return -ENODEV;
|
|
|
+ dev = dev_file_info->device;
|
|
|
if (dev == NULL)
|
|
|
- return;
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
mutex_lock(&dev->mutex);
|
|
|
- comedi_device_detach(dev);
|
|
|
- mutex_unlock(&dev->mutex);
|
|
|
- mutex_destroy(&dev->mutex);
|
|
|
-}
|
|
|
|
|
|
-int comedi_alloc_board_minor(struct device *hardware_device)
|
|
|
-{
|
|
|
- unsigned long flags;
|
|
|
- struct comedi_device_file_info *info;
|
|
|
- struct device *csdev;
|
|
|
- unsigned i;
|
|
|
- int retval;
|
|
|
+ if (dev->subdevices) {
|
|
|
+ for (i = 0; i < dev->n_subdevices; i++) {
|
|
|
+ s = dev->subdevices + i;
|
|
|
|
|
|
- info = kzalloc(sizeof(struct comedi_device_file_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;
|
|
|
- }
|
|
|
- comedi_device_init(info->device);
|
|
|
- spin_lock_irqsave(&comedi_file_info_table_lock, flags);
|
|
|
- for (i = 0; i < COMEDI_NUM_BOARD_MINORS; ++i) {
|
|
|
- if (comedi_file_info_table[i] == NULL) {
|
|
|
- comedi_file_info_table[i] = info;
|
|
|
- break;
|
|
|
+ if (s->busy == file)
|
|
|
+ do_cancel(dev, s);
|
|
|
+ if (s->lock == file)
|
|
|
+ s->lock = NULL;
|
|
|
}
|
|
|
}
|
|
|
- spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
|
|
|
- if (i == COMEDI_NUM_BOARD_MINORS) {
|
|
|
- comedi_device_cleanup(info->device);
|
|
|
- kfree(info->device);
|
|
|
- kfree(info);
|
|
|
- printk(KERN_ERR
|
|
|
- "comedi: error: "
|
|
|
- "ran out of minor numbers for board device files.\n");
|
|
|
- return -EBUSY;
|
|
|
- }
|
|
|
- info->device->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);
|
|
|
- retval = device_create_file(csdev, &dev_attr_max_read_buffer_kb);
|
|
|
- if (retval) {
|
|
|
- printk(KERN_ERR
|
|
|
- "comedi: "
|
|
|
- "failed to create sysfs attribute file \"%s\".\n",
|
|
|
- dev_attr_max_read_buffer_kb.attr.name);
|
|
|
- comedi_free_board_minor(i);
|
|
|
- return retval;
|
|
|
- }
|
|
|
- retval = device_create_file(csdev, &dev_attr_read_buffer_kb);
|
|
|
- if (retval) {
|
|
|
- printk(KERN_ERR
|
|
|
- "comedi: "
|
|
|
- "failed to create sysfs attribute file \"%s\".\n",
|
|
|
- dev_attr_read_buffer_kb.attr.name);
|
|
|
- comedi_free_board_minor(i);
|
|
|
- return retval;
|
|
|
- }
|
|
|
- retval = device_create_file(csdev, &dev_attr_max_write_buffer_kb);
|
|
|
- if (retval) {
|
|
|
- printk(KERN_ERR
|
|
|
- "comedi: "
|
|
|
- "failed to create sysfs attribute file \"%s\".\n",
|
|
|
- dev_attr_max_write_buffer_kb.attr.name);
|
|
|
- comedi_free_board_minor(i);
|
|
|
- return retval;
|
|
|
- }
|
|
|
- retval = device_create_file(csdev, &dev_attr_write_buffer_kb);
|
|
|
- if (retval) {
|
|
|
- printk(KERN_ERR
|
|
|
- "comedi: "
|
|
|
- "failed to create sysfs attribute file \"%s\".\n",
|
|
|
- dev_attr_write_buffer_kb.attr.name);
|
|
|
- comedi_free_board_minor(i);
|
|
|
- return retval;
|
|
|
- }
|
|
|
- return i;
|
|
|
-}
|
|
|
+ if (dev->attached && dev->use_count == 1 && dev->close)
|
|
|
+ dev->close(dev);
|
|
|
|
|
|
-void comedi_free_board_minor(unsigned minor)
|
|
|
-{
|
|
|
- unsigned long flags;
|
|
|
- struct comedi_device_file_info *info;
|
|
|
+ module_put(THIS_MODULE);
|
|
|
+ if (dev->attached)
|
|
|
+ module_put(dev->driver->module);
|
|
|
|
|
|
- BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS);
|
|
|
- spin_lock_irqsave(&comedi_file_info_table_lock, flags);
|
|
|
- info = comedi_file_info_table[minor];
|
|
|
- comedi_file_info_table[minor] = NULL;
|
|
|
- spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
|
|
|
+ dev->use_count--;
|
|
|
|
|
|
- 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);
|
|
|
- }
|
|
|
-}
|
|
|
+ mutex_unlock(&dev->mutex);
|
|
|
|
|
|
-int comedi_alloc_subdevice_minor(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s)
|
|
|
-{
|
|
|
- unsigned long flags;
|
|
|
- struct comedi_device_file_info *info;
|
|
|
- struct device *csdev;
|
|
|
- unsigned i;
|
|
|
- int retval;
|
|
|
+ if (file->f_flags & FASYNC)
|
|
|
+ comedi_fasync(-1, file, 0);
|
|
|
|
|
|
- info = kmalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
|
|
|
- if (info == NULL)
|
|
|
- return -ENOMEM;
|
|
|
- info->device = dev;
|
|
|
- info->read_subdevice = s;
|
|
|
- info->write_subdevice = s;
|
|
|
- spin_lock_irqsave(&comedi_file_info_table_lock, flags);
|
|
|
- for (i = COMEDI_FIRST_SUBDEVICE_MINOR; i < COMEDI_NUM_MINORS; ++i) {
|
|
|
- if (comedi_file_info_table[i] == NULL) {
|
|
|
- comedi_file_info_table[i] = info;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
|
|
|
- if (i == COMEDI_NUM_MINORS) {
|
|
|
- kfree(info);
|
|
|
- printk(KERN_ERR
|
|
|
- "comedi: error: "
|
|
|
- "ran out of minor numbers for board device files.\n");
|
|
|
- return -EBUSY;
|
|
|
- }
|
|
|
- s->minor = i;
|
|
|
- csdev = device_create(comedi_class, dev->class_dev,
|
|
|
- MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i_subd%i",
|
|
|
- dev->minor, (int)(s - dev->subdevices));
|
|
|
- if (!IS_ERR(csdev))
|
|
|
- s->class_dev = csdev;
|
|
|
- dev_set_drvdata(csdev, info);
|
|
|
- retval = device_create_file(csdev, &dev_attr_max_read_buffer_kb);
|
|
|
- if (retval) {
|
|
|
- printk(KERN_ERR
|
|
|
- "comedi: "
|
|
|
- "failed to create sysfs attribute file \"%s\".\n",
|
|
|
- dev_attr_max_read_buffer_kb.attr.name);
|
|
|
- comedi_free_subdevice_minor(s);
|
|
|
- return retval;
|
|
|
- }
|
|
|
- retval = device_create_file(csdev, &dev_attr_read_buffer_kb);
|
|
|
- if (retval) {
|
|
|
- printk(KERN_ERR
|
|
|
- "comedi: "
|
|
|
- "failed to create sysfs attribute file \"%s\".\n",
|
|
|
- dev_attr_read_buffer_kb.attr.name);
|
|
|
- comedi_free_subdevice_minor(s);
|
|
|
- return retval;
|
|
|
- }
|
|
|
- retval = device_create_file(csdev, &dev_attr_max_write_buffer_kb);
|
|
|
- if (retval) {
|
|
|
- printk(KERN_ERR
|
|
|
- "comedi: "
|
|
|
- "failed to create sysfs attribute file \"%s\".\n",
|
|
|
- dev_attr_max_write_buffer_kb.attr.name);
|
|
|
- comedi_free_subdevice_minor(s);
|
|
|
- return retval;
|
|
|
- }
|
|
|
- retval = device_create_file(csdev, &dev_attr_write_buffer_kb);
|
|
|
- if (retval) {
|
|
|
- printk(KERN_ERR
|
|
|
- "comedi: "
|
|
|
- "failed to create sysfs attribute file \"%s\".\n",
|
|
|
- dev_attr_write_buffer_kb.attr.name);
|
|
|
- comedi_free_subdevice_minor(s);
|
|
|
- return retval;
|
|
|
- }
|
|
|
- return i;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
-void comedi_free_subdevice_minor(struct comedi_subdevice *s)
|
|
|
+static int comedi_fasync(int fd, struct file *file, int on)
|
|
|
{
|
|
|
- unsigned long flags;
|
|
|
- struct comedi_device_file_info *info;
|
|
|
-
|
|
|
- if (s == NULL)
|
|
|
- return;
|
|
|
- if (s->minor < 0)
|
|
|
- return;
|
|
|
-
|
|
|
- BUG_ON(s->minor >= COMEDI_NUM_MINORS);
|
|
|
- BUG_ON(s->minor < COMEDI_FIRST_SUBDEVICE_MINOR);
|
|
|
+ const unsigned minor = iminor(file->f_dentry->d_inode);
|
|
|
+ struct comedi_device_file_info *dev_file_info;
|
|
|
+ struct comedi_device *dev;
|
|
|
+ dev_file_info = comedi_get_device_file_info(minor);
|
|
|
|
|
|
- spin_lock_irqsave(&comedi_file_info_table_lock, flags);
|
|
|
- info = comedi_file_info_table[s->minor];
|
|
|
- comedi_file_info_table[s->minor] = NULL;
|
|
|
- spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
|
|
|
+ if (dev_file_info == NULL)
|
|
|
+ return -ENODEV;
|
|
|
+ dev = dev_file_info->device;
|
|
|
+ if (dev == NULL)
|
|
|
+ return -ENODEV;
|
|
|
|
|
|
- if (s->class_dev) {
|
|
|
- device_destroy(comedi_class, MKDEV(COMEDI_MAJOR, s->minor));
|
|
|
- s->class_dev = NULL;
|
|
|
- }
|
|
|
- kfree(info);
|
|
|
+ return fasync_helper(fd, file, on, &dev->async_queue);
|
|
|
}
|
|
|
|
|
|
-struct comedi_device_file_info *comedi_get_device_file_info(unsigned minor)
|
|
|
+const struct file_operations comedi_fops = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .unlocked_ioctl = comedi_unlocked_ioctl,
|
|
|
+ .compat_ioctl = comedi_compat_ioctl,
|
|
|
+ .open = comedi_open,
|
|
|
+ .release = comedi_close,
|
|
|
+ .read = comedi_read,
|
|
|
+ .write = comedi_write,
|
|
|
+ .mmap = comedi_mmap,
|
|
|
+ .poll = comedi_poll,
|
|
|
+ .fasync = comedi_fasync,
|
|
|
+ .llseek = noop_llseek,
|
|
|
+};
|
|
|
+
|
|
|
+struct class *comedi_class;
|
|
|
+static struct cdev comedi_cdev;
|
|
|
+
|
|
|
+static void comedi_cleanup_legacy_minors(void)
|
|
|
{
|
|
|
- unsigned long flags;
|
|
|
- struct comedi_device_file_info *info;
|
|
|
+ unsigned i;
|
|
|
|
|
|
- BUG_ON(minor >= COMEDI_NUM_MINORS);
|
|
|
- spin_lock_irqsave(&comedi_file_info_table_lock, flags);
|
|
|
- info = comedi_file_info_table[minor];
|
|
|
- spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
|
|
|
- return info;
|
|
|
+ for (i = 0; i < comedi_num_legacy_minors; i++)
|
|
|
+ comedi_free_board_minor(i);
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(comedi_get_device_file_info);
|
|
|
|
|
|
-static int resize_async_buffer(struct comedi_device *dev,
|
|
|
- struct comedi_subdevice *s,
|
|
|
- struct comedi_async *async, unsigned new_size)
|
|
|
+static int __init comedi_init(void)
|
|
|
{
|
|
|
+ int i;
|
|
|
int retval;
|
|
|
|
|
|
- if (new_size > async->max_bufsize)
|
|
|
- return -EPERM;
|
|
|
+ printk(KERN_INFO "comedi: version " COMEDI_RELEASE
|
|
|
+ " - http://www.comedi.org\n");
|
|
|
|
|
|
- if (s->busy) {
|
|
|
- DPRINTK("subdevice is busy, cannot resize buffer\n");
|
|
|
- return -EBUSY;
|
|
|
- }
|
|
|
- if (async->mmap_count) {
|
|
|
- DPRINTK("subdevice is mmapped, cannot resize buffer\n");
|
|
|
- return -EBUSY;
|
|
|
+ if (comedi_num_legacy_minors < 0 ||
|
|
|
+ comedi_num_legacy_minors > COMEDI_NUM_BOARD_MINORS) {
|
|
|
+ printk(KERN_ERR "comedi: error: invalid value for module "
|
|
|
+ "parameter \"comedi_num_legacy_minors\". Valid values "
|
|
|
+ "are 0 through %i.\n", COMEDI_NUM_BOARD_MINORS);
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- if (!async->prealloc_buf)
|
|
|
- return -EINVAL;
|
|
|
+ /*
|
|
|
+ * comedi is unusable if both comedi_autoconfig and
|
|
|
+ * comedi_num_legacy_minors are zero, so we might as well adjust the
|
|
|
+ * defaults in that case
|
|
|
+ */
|
|
|
+ if (comedi_autoconfig == 0 && comedi_num_legacy_minors == 0)
|
|
|
+ comedi_num_legacy_minors = 16;
|
|
|
|
|
|
- /* make sure buffer is an integral number of pages
|
|
|
- * (we round up) */
|
|
|
- new_size = (new_size + PAGE_SIZE - 1) & PAGE_MASK;
|
|
|
+ memset(comedi_file_info_table, 0,
|
|
|
+ sizeof(struct comedi_device_file_info *) * COMEDI_NUM_MINORS);
|
|
|
|
|
|
- retval = comedi_buf_alloc(dev, s, new_size);
|
|
|
- if (retval < 0)
|
|
|
- return retval;
|
|
|
+ retval = register_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
|
|
|
+ COMEDI_NUM_MINORS, "comedi");
|
|
|
+ if (retval)
|
|
|
+ return -EIO;
|
|
|
+ cdev_init(&comedi_cdev, &comedi_fops);
|
|
|
+ comedi_cdev.owner = THIS_MODULE;
|
|
|
+ kobject_set_name(&comedi_cdev.kobj, "comedi");
|
|
|
+ if (cdev_add(&comedi_cdev, MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS)) {
|
|
|
+ unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
|
|
|
+ COMEDI_NUM_MINORS);
|
|
|
+ return -EIO;
|
|
|
+ }
|
|
|
+ comedi_class = class_create(THIS_MODULE, "comedi");
|
|
|
+ if (IS_ERR(comedi_class)) {
|
|
|
+ printk(KERN_ERR "comedi: failed to create class");
|
|
|
+ cdev_del(&comedi_cdev);
|
|
|
+ unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
|
|
|
+ COMEDI_NUM_MINORS);
|
|
|
+ return PTR_ERR(comedi_class);
|
|
|
+ }
|
|
|
|
|
|
- if (s->buf_change) {
|
|
|
- retval = s->buf_change(dev, s, new_size);
|
|
|
- if (retval < 0)
|
|
|
- return retval;
|
|
|
+ comedi_class->dev_attrs = comedi_dev_attrs;
|
|
|
+
|
|
|
+ /* XXX requires /proc interface */
|
|
|
+ comedi_proc_init();
|
|
|
+
|
|
|
+ /* 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) {
|
|
|
+ comedi_cleanup_legacy_minors();
|
|
|
+ cdev_del(&comedi_cdev);
|
|
|
+ unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
|
|
|
+ COMEDI_NUM_MINORS);
|
|
|
+ return minor;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- DPRINTK("comedi%i subd %d buffer resized to %i bytes\n",
|
|
|
- dev->minor, (int)(s - dev->subdevices), async->prealloc_bufsz);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-/* sysfs attribute files */
|
|
|
+static void __exit comedi_cleanup(void)
|
|
|
+{
|
|
|
+ int i;
|
|
|
|
|
|
-static const unsigned bytes_per_kibi = 1024;
|
|
|
+ comedi_cleanup_legacy_minors();
|
|
|
+ for (i = 0; i < COMEDI_NUM_MINORS; ++i)
|
|
|
+ BUG_ON(comedi_file_info_table[i]);
|
|
|
|
|
|
-static ssize_t show_max_read_buffer_kb(struct device *dev,
|
|
|
- struct device_attribute *attr, char *buf)
|
|
|
-{
|
|
|
- ssize_t retval;
|
|
|
- struct comedi_device_file_info *info = dev_get_drvdata(dev);
|
|
|
- unsigned max_buffer_size_kb = 0;
|
|
|
- struct comedi_subdevice *const read_subdevice =
|
|
|
- comedi_get_read_subdevice(info);
|
|
|
+ class_destroy(comedi_class);
|
|
|
+ cdev_del(&comedi_cdev);
|
|
|
+ unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS);
|
|
|
|
|
|
- mutex_lock(&info->device->mutex);
|
|
|
- if (read_subdevice &&
|
|
|
- (read_subdevice->subdev_flags & SDF_CMD_READ) &&
|
|
|
- read_subdevice->async) {
|
|
|
- max_buffer_size_kb = read_subdevice->async->max_bufsize /
|
|
|
- bytes_per_kibi;
|
|
|
- }
|
|
|
- retval = snprintf(buf, PAGE_SIZE, "%i\n", max_buffer_size_kb);
|
|
|
- mutex_unlock(&info->device->mutex);
|
|
|
+ comedi_proc_cleanup();
|
|
|
+}
|
|
|
|
|
|
- return retval;
|
|
|
+module_init(comedi_init);
|
|
|
+module_exit(comedi_cleanup);
|
|
|
+
|
|
|
+void comedi_error(const struct comedi_device *dev, const char *s)
|
|
|
+{
|
|
|
+ printk(KERN_ERR "comedi%d: %s: %s\n", dev->minor,
|
|
|
+ dev->driver->driver_name, s);
|
|
|
}
|
|
|
+EXPORT_SYMBOL(comedi_error);
|
|
|
|
|
|
-static ssize_t store_max_read_buffer_kb(struct device *dev,
|
|
|
- struct device_attribute *attr,
|
|
|
- const char *buf, size_t count)
|
|
|
+void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s)
|
|
|
{
|
|
|
- struct comedi_device_file_info *info = dev_get_drvdata(dev);
|
|
|
- unsigned int new_max_size_kb;
|
|
|
- unsigned int new_max_size;
|
|
|
- int ret;
|
|
|
- struct comedi_subdevice *const read_subdevice =
|
|
|
- comedi_get_read_subdevice(info);
|
|
|
+ struct comedi_async *async = s->async;
|
|
|
+ unsigned runflags = 0;
|
|
|
+ unsigned runflags_mask = 0;
|
|
|
|
|
|
- ret = kstrtouint(buf, 10, &new_max_size_kb);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
- if (new_max_size_kb > (UINT_MAX / bytes_per_kibi))
|
|
|
- return -EINVAL;
|
|
|
- new_max_size = new_max_size_kb * bytes_per_kibi;
|
|
|
+ /* DPRINTK("comedi_event 0x%x\n",mask); */
|
|
|
|
|
|
- mutex_lock(&info->device->mutex);
|
|
|
- if (read_subdevice == NULL ||
|
|
|
- (read_subdevice->subdev_flags & SDF_CMD_READ) == 0 ||
|
|
|
- read_subdevice->async == NULL) {
|
|
|
- mutex_unlock(&info->device->mutex);
|
|
|
- return -EINVAL;
|
|
|
+ if ((comedi_get_subdevice_runflags(s) & SRF_RUNNING) == 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (s->
|
|
|
+ async->events & (COMEDI_CB_EOA | COMEDI_CB_ERROR |
|
|
|
+ COMEDI_CB_OVERFLOW)) {
|
|
|
+ runflags_mask |= SRF_RUNNING;
|
|
|
+ }
|
|
|
+ /* remember if an error event has occurred, so an error
|
|
|
+ * can be returned the next time the user does a read() */
|
|
|
+ if (s->async->events & (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) {
|
|
|
+ runflags_mask |= SRF_ERROR;
|
|
|
+ runflags |= SRF_ERROR;
|
|
|
+ }
|
|
|
+ if (runflags_mask) {
|
|
|
+ /*sets SRF_ERROR and SRF_RUNNING together atomically */
|
|
|
+ comedi_set_subdevice_runflags(s, runflags_mask, runflags);
|
|
|
}
|
|
|
- read_subdevice->async->max_bufsize = new_max_size;
|
|
|
- mutex_unlock(&info->device->mutex);
|
|
|
|
|
|
- return count;
|
|
|
+ if (async->cb_mask & s->async->events) {
|
|
|
+ if (comedi_get_subdevice_runflags(s) & SRF_USER) {
|
|
|
+ wake_up_interruptible(&async->wait_head);
|
|
|
+ if (s->subdev_flags & SDF_CMD_READ)
|
|
|
+ kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
|
|
|
+ if (s->subdev_flags & SDF_CMD_WRITE)
|
|
|
+ kill_fasync(&dev->async_queue, SIGIO, POLL_OUT);
|
|
|
+ } else {
|
|
|
+ if (async->cb_func)
|
|
|
+ async->cb_func(s->async->events, async->cb_arg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ s->async->events = 0;
|
|
|
}
|
|
|
+EXPORT_SYMBOL(comedi_event);
|
|
|
|
|
|
-static struct device_attribute dev_attr_max_read_buffer_kb = {
|
|
|
- .attr = {
|
|
|
- .name = "max_read_buffer_kb",
|
|
|
- .mode = S_IRUGO | S_IWUSR},
|
|
|
- .show = &show_max_read_buffer_kb,
|
|
|
- .store = &store_max_read_buffer_kb
|
|
|
-};
|
|
|
-
|
|
|
-static ssize_t show_read_buffer_kb(struct device *dev,
|
|
|
- struct device_attribute *attr, char *buf)
|
|
|
+unsigned comedi_get_subdevice_runflags(struct comedi_subdevice *s)
|
|
|
{
|
|
|
- ssize_t retval;
|
|
|
- struct comedi_device_file_info *info = dev_get_drvdata(dev);
|
|
|
- unsigned buffer_size_kb = 0;
|
|
|
- struct comedi_subdevice *const read_subdevice =
|
|
|
- comedi_get_read_subdevice(info);
|
|
|
-
|
|
|
- mutex_lock(&info->device->mutex);
|
|
|
- if (read_subdevice &&
|
|
|
- (read_subdevice->subdev_flags & SDF_CMD_READ) &&
|
|
|
- read_subdevice->async) {
|
|
|
- buffer_size_kb = read_subdevice->async->prealloc_bufsz /
|
|
|
- bytes_per_kibi;
|
|
|
- }
|
|
|
- retval = snprintf(buf, PAGE_SIZE, "%i\n", buffer_size_kb);
|
|
|
- mutex_unlock(&info->device->mutex);
|
|
|
+ unsigned long flags;
|
|
|
+ unsigned runflags;
|
|
|
|
|
|
- return retval;
|
|
|
+ spin_lock_irqsave(&s->spin_lock, flags);
|
|
|
+ runflags = s->runflags;
|
|
|
+ spin_unlock_irqrestore(&s->spin_lock, flags);
|
|
|
+ return runflags;
|
|
|
}
|
|
|
+EXPORT_SYMBOL(comedi_get_subdevice_runflags);
|
|
|
|
|
|
-static ssize_t store_read_buffer_kb(struct device *dev,
|
|
|
- struct device_attribute *attr,
|
|
|
- const char *buf, size_t count)
|
|
|
+static int is_device_busy(struct comedi_device *dev)
|
|
|
{
|
|
|
- struct comedi_device_file_info *info = dev_get_drvdata(dev);
|
|
|
- unsigned int new_size_kb;
|
|
|
- unsigned int new_size;
|
|
|
- int retval;
|
|
|
- int ret;
|
|
|
- struct comedi_subdevice *const read_subdevice =
|
|
|
- comedi_get_read_subdevice(info);
|
|
|
+ struct comedi_subdevice *s;
|
|
|
+ int i;
|
|
|
|
|
|
- ret = kstrtouint(buf, 10, &new_size_kb);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
- if (new_size_kb > (UINT_MAX / bytes_per_kibi))
|
|
|
- return -EINVAL;
|
|
|
- new_size = new_size_kb * bytes_per_kibi;
|
|
|
+ if (!dev->attached)
|
|
|
+ return 0;
|
|
|
|
|
|
- mutex_lock(&info->device->mutex);
|
|
|
- if (read_subdevice == NULL ||
|
|
|
- (read_subdevice->subdev_flags & SDF_CMD_READ) == 0 ||
|
|
|
- read_subdevice->async == NULL) {
|
|
|
- mutex_unlock(&info->device->mutex);
|
|
|
- return -EINVAL;
|
|
|
+ for (i = 0; i < dev->n_subdevices; i++) {
|
|
|
+ s = dev->subdevices + i;
|
|
|
+ if (s->busy)
|
|
|
+ return 1;
|
|
|
+ if (s->async && s->async->mmap_count)
|
|
|
+ return 1;
|
|
|
}
|
|
|
- retval = resize_async_buffer(info->device, read_subdevice,
|
|
|
- read_subdevice->async, new_size);
|
|
|
- mutex_unlock(&info->device->mutex);
|
|
|
|
|
|
- if (retval < 0)
|
|
|
- return retval;
|
|
|
- return count;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
-static struct device_attribute dev_attr_read_buffer_kb = {
|
|
|
- .attr = {
|
|
|
- .name = "read_buffer_kb",
|
|
|
- .mode = S_IRUGO | S_IWUSR | S_IWGRP},
|
|
|
- .show = &show_read_buffer_kb,
|
|
|
- .store = &store_read_buffer_kb
|
|
|
-};
|
|
|
+static void comedi_device_init(struct comedi_device *dev)
|
|
|
+{
|
|
|
+ memset(dev, 0, sizeof(struct comedi_device));
|
|
|
+ spin_lock_init(&dev->spinlock);
|
|
|
+ mutex_init(&dev->mutex);
|
|
|
+ dev->minor = -1;
|
|
|
+}
|
|
|
|
|
|
-static ssize_t show_max_write_buffer_kb(struct device *dev,
|
|
|
- struct device_attribute *attr,
|
|
|
- char *buf)
|
|
|
+static void comedi_device_cleanup(struct comedi_device *dev)
|
|
|
{
|
|
|
- ssize_t retval;
|
|
|
- struct comedi_device_file_info *info = dev_get_drvdata(dev);
|
|
|
- unsigned max_buffer_size_kb = 0;
|
|
|
- struct comedi_subdevice *const write_subdevice =
|
|
|
- comedi_get_write_subdevice(info);
|
|
|
+ if (dev == NULL)
|
|
|
+ return;
|
|
|
+ mutex_lock(&dev->mutex);
|
|
|
+ comedi_device_detach(dev);
|
|
|
+ mutex_unlock(&dev->mutex);
|
|
|
+ mutex_destroy(&dev->mutex);
|
|
|
+}
|
|
|
|
|
|
- mutex_lock(&info->device->mutex);
|
|
|
- if (write_subdevice &&
|
|
|
- (write_subdevice->subdev_flags & SDF_CMD_WRITE) &&
|
|
|
- write_subdevice->async) {
|
|
|
- max_buffer_size_kb = write_subdevice->async->max_bufsize /
|
|
|
- bytes_per_kibi;
|
|
|
+int comedi_alloc_board_minor(struct device *hardware_device)
|
|
|
+{
|
|
|
+ struct comedi_device_file_info *info;
|
|
|
+ struct device *csdev;
|
|
|
+ unsigned i;
|
|
|
+
|
|
|
+ info = kzalloc(sizeof(struct comedi_device_file_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;
|
|
|
}
|
|
|
- retval = snprintf(buf, PAGE_SIZE, "%i\n", max_buffer_size_kb);
|
|
|
- mutex_unlock(&info->device->mutex);
|
|
|
+ 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;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ spin_unlock(&comedi_file_info_table_lock);
|
|
|
+ if (i == COMEDI_NUM_BOARD_MINORS) {
|
|
|
+ comedi_device_cleanup(info->device);
|
|
|
+ kfree(info->device);
|
|
|
+ kfree(info);
|
|
|
+ printk(KERN_ERR
|
|
|
+ "comedi: error: "
|
|
|
+ "ran out of minor numbers for board device files.\n");
|
|
|
+ return -EBUSY;
|
|
|
+ }
|
|
|
+ info->device->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);
|
|
|
|
|
|
- return retval;
|
|
|
+ return i;
|
|
|
}
|
|
|
|
|
|
-static ssize_t store_max_write_buffer_kb(struct device *dev,
|
|
|
- struct device_attribute *attr,
|
|
|
- const char *buf, size_t count)
|
|
|
+void comedi_free_board_minor(unsigned minor)
|
|
|
{
|
|
|
- struct comedi_device_file_info *info = dev_get_drvdata(dev);
|
|
|
- unsigned int new_max_size_kb;
|
|
|
- unsigned int new_max_size;
|
|
|
- int ret;
|
|
|
- struct comedi_subdevice *const write_subdevice =
|
|
|
- comedi_get_write_subdevice(info);
|
|
|
+ struct comedi_device_file_info *info;
|
|
|
|
|
|
- ret = kstrtouint(buf, 10, &new_max_size_kb);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
- if (new_max_size_kb > (UINT_MAX / bytes_per_kibi))
|
|
|
- return -EINVAL;
|
|
|
- new_max_size = new_max_size_kb * bytes_per_kibi;
|
|
|
+ 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);
|
|
|
|
|
|
- mutex_lock(&info->device->mutex);
|
|
|
- if (write_subdevice == NULL ||
|
|
|
- (write_subdevice->subdev_flags & SDF_CMD_WRITE) == 0 ||
|
|
|
- write_subdevice->async == NULL) {
|
|
|
- mutex_unlock(&info->device->mutex);
|
|
|
- return -EINVAL;
|
|
|
+ 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);
|
|
|
}
|
|
|
- write_subdevice->async->max_bufsize = new_max_size;
|
|
|
- mutex_unlock(&info->device->mutex);
|
|
|
-
|
|
|
- return count;
|
|
|
}
|
|
|
|
|
|
-static struct device_attribute dev_attr_max_write_buffer_kb = {
|
|
|
- .attr = {
|
|
|
- .name = "max_write_buffer_kb",
|
|
|
- .mode = S_IRUGO | S_IWUSR},
|
|
|
- .show = &show_max_write_buffer_kb,
|
|
|
- .store = &store_max_write_buffer_kb
|
|
|
-};
|
|
|
+int comedi_find_board_minor(struct device *hardware_device)
|
|
|
+{
|
|
|
+ int minor;
|
|
|
+ struct comedi_device_file_info *info;
|
|
|
|
|
|
-static ssize_t show_write_buffer_kb(struct device *dev,
|
|
|
- struct device_attribute *attr, char *buf)
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ spin_unlock(&comedi_file_info_table_lock);
|
|
|
+ }
|
|
|
+ return -ENODEV;
|
|
|
+}
|
|
|
+
|
|
|
+int comedi_alloc_subdevice_minor(struct comedi_device *dev,
|
|
|
+ struct comedi_subdevice *s)
|
|
|
{
|
|
|
- ssize_t retval;
|
|
|
- struct comedi_device_file_info *info = dev_get_drvdata(dev);
|
|
|
- unsigned buffer_size_kb = 0;
|
|
|
- struct comedi_subdevice *const write_subdevice =
|
|
|
- comedi_get_write_subdevice(info);
|
|
|
+ struct comedi_device_file_info *info;
|
|
|
+ struct device *csdev;
|
|
|
+ unsigned i;
|
|
|
|
|
|
- mutex_lock(&info->device->mutex);
|
|
|
- if (write_subdevice &&
|
|
|
- (write_subdevice->subdev_flags & SDF_CMD_WRITE) &&
|
|
|
- write_subdevice->async) {
|
|
|
- buffer_size_kb = write_subdevice->async->prealloc_bufsz /
|
|
|
- bytes_per_kibi;
|
|
|
+ info = kmalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
|
|
|
+ if (info == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+ info->device = dev;
|
|
|
+ info->read_subdevice = s;
|
|
|
+ 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;
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
- retval = snprintf(buf, PAGE_SIZE, "%i\n", buffer_size_kb);
|
|
|
- mutex_unlock(&info->device->mutex);
|
|
|
+ spin_unlock(&comedi_file_info_table_lock);
|
|
|
+ if (i == COMEDI_NUM_MINORS) {
|
|
|
+ kfree(info);
|
|
|
+ printk(KERN_ERR
|
|
|
+ "comedi: error: "
|
|
|
+ "ran out of minor numbers for board device files.\n");
|
|
|
+ return -EBUSY;
|
|
|
+ }
|
|
|
+ s->minor = i;
|
|
|
+ csdev = device_create(comedi_class, dev->class_dev,
|
|
|
+ MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i_subd%i",
|
|
|
+ dev->minor, (int)(s - dev->subdevices));
|
|
|
+ if (!IS_ERR(csdev))
|
|
|
+ s->class_dev = csdev;
|
|
|
+ dev_set_drvdata(csdev, info);
|
|
|
|
|
|
- return retval;
|
|
|
+ return i;
|
|
|
}
|
|
|
|
|
|
-static ssize_t store_write_buffer_kb(struct device *dev,
|
|
|
- struct device_attribute *attr,
|
|
|
- const char *buf, size_t count)
|
|
|
+void comedi_free_subdevice_minor(struct comedi_subdevice *s)
|
|
|
{
|
|
|
- struct comedi_device_file_info *info = dev_get_drvdata(dev);
|
|
|
- unsigned int new_size_kb;
|
|
|
- unsigned int new_size;
|
|
|
- int retval;
|
|
|
- int ret;
|
|
|
- struct comedi_subdevice *const write_subdevice =
|
|
|
- comedi_get_write_subdevice(info);
|
|
|
+ struct comedi_device_file_info *info;
|
|
|
|
|
|
- ret = kstrtouint(buf, 10, &new_size_kb);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
- if (new_size_kb > (UINT_MAX / bytes_per_kibi))
|
|
|
- return -EINVAL;
|
|
|
- new_size = ((uint64_t) new_size_kb) * bytes_per_kibi;
|
|
|
+ if (s == NULL)
|
|
|
+ return;
|
|
|
+ if (s->minor < 0)
|
|
|
+ return;
|
|
|
|
|
|
- mutex_lock(&info->device->mutex);
|
|
|
- if (write_subdevice == NULL ||
|
|
|
- (write_subdevice->subdev_flags & SDF_CMD_WRITE) == 0 ||
|
|
|
- write_subdevice->async == NULL) {
|
|
|
- mutex_unlock(&info->device->mutex);
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
- retval = resize_async_buffer(info->device, write_subdevice,
|
|
|
- write_subdevice->async, new_size);
|
|
|
- mutex_unlock(&info->device->mutex);
|
|
|
+ BUG_ON(s->minor >= COMEDI_NUM_MINORS);
|
|
|
+ BUG_ON(s->minor < COMEDI_FIRST_SUBDEVICE_MINOR);
|
|
|
|
|
|
- if (retval < 0)
|
|
|
- return retval;
|
|
|
- return count;
|
|
|
+ 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);
|
|
|
+
|
|
|
+ if (s->class_dev) {
|
|
|
+ device_destroy(comedi_class, MKDEV(COMEDI_MAJOR, s->minor));
|
|
|
+ s->class_dev = NULL;
|
|
|
+ }
|
|
|
+ kfree(info);
|
|
|
}
|
|
|
|
|
|
-static struct device_attribute dev_attr_write_buffer_kb = {
|
|
|
- .attr = {
|
|
|
- .name = "write_buffer_kb",
|
|
|
- .mode = S_IRUGO | S_IWUSR | S_IWGRP},
|
|
|
- .show = &show_write_buffer_kb,
|
|
|
- .store = &store_write_buffer_kb
|
|
|
-};
|
|
|
+struct comedi_device_file_info *comedi_get_device_file_info(unsigned minor)
|
|
|
+{
|
|
|
+ struct comedi_device_file_info *info;
|
|
|
+
|
|
|
+ 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;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(comedi_get_device_file_info);
|