From 45641527371a7bf88dac9edecef704d8d9e859d3 Mon Sep 17 00:00:00 2001 From: Frank Mori Hess Date: Thu, 2 Apr 2009 19:14:55 +0000 Subject: [PATCH] Added sysfs attribute files for setting and querying subdevice buffer sizes. --- comedi/comedi_fops.c | 405 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 369 insertions(+), 36 deletions(-) diff --git a/comedi/comedi_fops.c b/comedi/comedi_fops.c index 198f4d99..d2f5ac1c 100644 --- a/comedi/comedi_fops.c +++ b/comedi/comedi_fops.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -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 +}; -- 2.26.2