Added sysfs attribute files for setting and querying subdevice
authorFrank Mori Hess <fmhess@speakeasy.net>
Thu, 2 Apr 2009 19:14:55 +0000 (19:14 +0000)
committerFrank Mori Hess <fmhess@speakeasy.net>
Thu, 2 Apr 2009 19:14:55 +0000 (19:14 +0000)
buffer sizes.

comedi/comedi_fops.c

index 198f4d992df645a45556847ab95165a6a96443da..d2f5ac1c26379a09c60c6c46020d298fa62b8241 100644 (file)
@@ -44,6 +44,7 @@
 #include <linux/fs.h>
 #include <linux/comedidev.h>
 #include <linux/cdev.h>
+#include <linux/stat.h>
 
 #include <asm/io.h>
 #include <asm/uaccess.h>
@@ -91,6 +92,14 @@ static int do_cancel(comedi_device * dev, comedi_subdevice * s);
 static int comedi_fasync(int fd, struct file *file, int on);
 
 static int is_device_busy(comedi_device * dev);
+static int resize_async_buffer(comedi_device *dev,
+       comedi_subdevice *s, comedi_async *async, unsigned new_size);
+
+//declarations for sysfs attribute files
+struct device_attribute dev_attr_max_read_buffer_kb;
+struct device_attribute dev_attr_read_buffer_kb;
+struct device_attribute dev_attr_max_write_buffer_kb;
+struct device_attribute dev_attr_write_buffer_kb;
 
 #ifdef HAVE_UNLOCKED_IOCTL
 static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
@@ -107,7 +116,7 @@ static int comedi_ioctl(struct inode *inode, struct file *file,
 
        if(dev_file_info == NULL || dev_file_info->device == NULL) return -ENODEV;
        dev = dev_file_info->device;
-       
+
        mutex_lock(&dev->mutex);
 
        /* Device config is special, because it must work on
@@ -276,7 +285,7 @@ static int do_bufconfig_ioctl(comedi_device * dev, void *arg)
        comedi_bufconfig bc;
        comedi_async *async;
        comedi_subdevice *s;
-       int ret = 0;
+       int retval = 0;
 
        if (copy_from_user(&bc, arg, sizeof(comedi_bufconfig)))
                return -EFAULT;
@@ -302,37 +311,8 @@ static int do_bufconfig_ioctl(comedi_device * dev, void *arg)
        }
 
        if (bc.size) {
-               if (bc.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) */
-               bc.size = (bc.size + PAGE_SIZE - 1) & PAGE_MASK;
-
-               ret = comedi_buf_alloc(dev, s, bc.size);
-               if (ret < 0)
-                       return ret;
-
-               if (s->buf_change) {
-                       ret = s->buf_change(dev, s, bc.size);
-                       if (ret < 0)
-                               return ret;
-               }
-
-               DPRINTK("comedi%i subd %d buffer resized to %i bytes\n",
-                       dev->minor, bc.subdevice, async->prealloc_bufsz);
+               retval = resize_async_buffer(dev, s, async, bc.size);
+               if(retval < 0) return retval;
        }
 
        bc.size = async->prealloc_bufsz;
@@ -1924,7 +1904,7 @@ static int __init comedi_init(void)
        {
                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),
@@ -2127,6 +2107,7 @@ int comedi_alloc_board_minor(struct device *hardware_device)
        struct comedi_device_file_info *info;
        device_create_result_type *csdev;
        unsigned i;
+       int retval;
 
        info = kzalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
        if(info == NULL) return -ENOMEM;
@@ -2152,7 +2133,7 @@ int comedi_alloc_board_minor(struct device *hardware_device)
                comedi_device_cleanup(info->device);
                kfree(info->device);
                kfree(info);
-               rt_printk("comedi: error: ran out of minor numbers for board device files.\n");
+               printk("comedi: error: ran out of minor numbers for board device files.\n");
                return -EBUSY;
        }
        info->device->minor = i;
@@ -2161,6 +2142,35 @@ int comedi_alloc_board_minor(struct device *hardware_device)
        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;
 }
 
@@ -2197,6 +2207,7 @@ int comedi_alloc_subdevice_minor(comedi_device *dev, comedi_subdevice *s)
        struct comedi_device_file_info *info;
        device_create_result_type *csdev;
        unsigned i;
+       int retval;
 
        info = kmalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
        if(info == NULL) return -ENOMEM;
@@ -2216,7 +2227,7 @@ int comedi_alloc_subdevice_minor(comedi_device *dev, comedi_subdevice *s)
        if(i == COMEDI_NUM_MINORS)
        {
                kfree(info);
-               rt_printk("comedi: error: ran out of minor numbers for board device files.\n");
+               printk("comedi: error: ran out of minor numbers for board device files.\n");
                return -EBUSY;
        }
        s->minor = i;
@@ -2226,6 +2237,35 @@ int comedi_alloc_subdevice_minor(comedi_device *dev, comedi_subdevice *s)
        {
                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;
 }
 
@@ -2264,3 +2304,296 @@ struct comedi_device_file_info *comedi_get_device_file_info(unsigned minor)
        comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
        return info;
 }
+
+static int resize_async_buffer(comedi_device *dev,
+       comedi_subdevice *s, 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, s - dev->subdevices, async->prealloc_bufsz);
+       return 0;
+}
+
+// sysfs attribute files
+
+static const unsigned bytes_per_kibi = 1024;
+
+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;
+       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;
+}
+
+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 long new_max_size_kb;
+       uint64_t new_max_size;
+       comedi_subdevice * const read_subdevice = comedi_get_read_subdevice(info);
+
+       if(strict_strtoul(buf, 10, &new_max_size_kb))
+       {
+               return -EINVAL;
+       }
+       if(new_max_size_kb != (uint32_t)new_max_size_kb) return -EINVAL;
+       new_max_size = ((uint64_t)new_max_size_kb) * bytes_per_kibi;
+       if(new_max_size != (uint32_t)new_max_size) return -EINVAL;
+
+       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;
+}
+
+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
+};
+
+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;
+       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;
+}
+
+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 long new_size_kb;
+       uint64_t new_size;
+       int retval;
+       comedi_subdevice * const read_subdevice = comedi_get_read_subdevice(info);
+
+       if(strict_strtoul(buf, 10, &new_size_kb))
+       {
+               return -EINVAL;
+       }
+       if(new_size_kb != (uint32_t)new_size_kb) return -EINVAL;
+       new_size = ((uint64_t)new_size_kb) * bytes_per_kibi;
+       if(new_size != (uint32_t)new_size) return -EINVAL;
+
+       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;
+}
+
+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
+};
+
+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;
+       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;
+}
+
+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 long new_max_size_kb;
+       uint64_t new_max_size;
+       comedi_subdevice * const write_subdevice = comedi_get_write_subdevice(info);
+
+       if(strict_strtoul(buf, 10, &new_max_size_kb))
+       {
+               return -EINVAL;
+       }
+       if(new_max_size_kb != (uint32_t)new_max_size_kb) return -EINVAL;
+       new_max_size = ((uint64_t)new_max_size_kb) * bytes_per_kibi;
+       if(new_max_size != (uint32_t)new_max_size) return -EINVAL;
+
+       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;
+}
+
+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
+};
+
+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;
+       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;
+}
+
+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 long new_size_kb;
+       uint64_t new_size;
+       int retval;
+       comedi_subdevice * const write_subdevice = comedi_get_write_subdevice(info);
+
+       if(strict_strtoul(buf, 10, &new_size_kb))
+       {
+               return -EINVAL;
+       }
+       if(new_size_kb != (uint32_t)new_size_kb) return -EINVAL;
+       new_size = ((uint64_t)new_size_kb) * bytes_per_kibi;
+       if(new_size != (uint32_t)new_size) return -EINVAL;
+
+       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;
+}
+
+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
+};