Staging: comedi: Added sysfs attribute files for setting and querying subdevice buffer sizes.

Signed-off-by: Frank Mori Hess <fmhess@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c
index 19dce2e..bb4a289 100644
--- a/drivers/staging/comedi/comedi_fops.c
+++ b/drivers/staging/comedi/comedi_fops.c
@@ -44,6 +44,7 @@
 #include <linux/fs.h>
 #include "comedidev.h"
 #include <linux/cdev.h>
+#include <linux/stat.h>
 
 #include <linux/io.h>
 #include <linux/uaccess.h>
@@ -92,6 +93,15 @@
 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);
+
+/* 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;
 
 #ifdef HAVE_UNLOCKED_IOCTL
 static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
@@ -277,7 +287,7 @@
 	struct comedi_bufconfig bc;
 	struct comedi_async *async;
 	struct comedi_subdevice *s;
-	int ret = 0;
+	int retval = 0;
 
 	if (copy_from_user(&bc, arg, sizeof(struct comedi_bufconfig)))
 		return -EFAULT;
@@ -303,37 +313,9 @@
 	}
 
 	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;
@@ -2132,6 +2114,7 @@
 	struct comedi_device_file_info *info;
 	struct device *csdev;
 	unsigned i;
+	int retval;
 
 	info = kzalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
 	if (info == NULL)
@@ -2154,8 +2137,7 @@
 		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(KERN_ERR "comedi: error: ran out of minor numbers for board device files.\n");
 		return -EBUSY;
 	}
 	info->device->minor = i;
@@ -2164,7 +2146,35 @@
 				     hardware_device, "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;
 }
 
@@ -2193,12 +2203,14 @@
 	}
 }
 
-int comedi_alloc_subdevice_minor(struct comedi_device *dev, struct comedi_subdevice *s)
+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;
 
 	info = kmalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
 	if (info == NULL)
@@ -2216,8 +2228,7 @@
 	comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
 	if (i == COMEDI_NUM_MINORS) {
 		kfree(info);
-		rt_printk
-		    ("comedi: error: ran out of minor numbers for board device files.\n");
+		printk(KERN_ERR "comedi: error: ran out of minor numbers for board device files.\n");
 		return -EBUSY;
 	}
 	s->minor = i;
@@ -2227,7 +2238,35 @@
 				     (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;
 }
 
@@ -2267,3 +2306,304 @@
 	comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
 	return info;
 }
+
+static int resize_async_buffer(struct comedi_device *dev,
+			       struct comedi_subdevice *s,
+			       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, 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 long new_max_size_kb;
+	uint64_t new_max_size;
+	struct 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;
+}
+
+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)
+{
+	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 long new_size_kb;
+	uint64_t new_size;
+	int retval;
+	struct 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;
+}
+
+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 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 long new_max_size_kb;
+	uint64_t new_max_size;
+	struct 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;
+}
+
+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
+};
+
+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 long new_size_kb;
+	uint64_t new_size;
+	int retval;
+	struct 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;
+}
+
+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
+};