Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 883db3d9 authored by Frank Mori Hess's avatar Frank Mori Hess Committed by Greg Kroah-Hartman
Browse files

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

parent 0d6e5dad
Loading
Loading
Loading
Loading
+379 −39
Original line number Original line Diff line number Diff line
@@ -44,6 +44,7 @@
#include <linux/fs.h>
#include <linux/fs.h>
#include "comedidev.h"
#include "comedidev.h"
#include <linux/cdev.h>
#include <linux/cdev.h>
#include <linux/stat.h>


#include <linux/io.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/uaccess.h>
@@ -92,6 +93,15 @@ 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 comedi_fasync(int fd, struct file *file, int on);


static int is_device_busy(struct comedi_device *dev);
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
#ifdef HAVE_UNLOCKED_IOCTL
static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
@@ -277,7 +287,7 @@ static int do_bufconfig_ioctl(struct comedi_device *dev, void *arg)
	struct comedi_bufconfig bc;
	struct comedi_bufconfig bc;
	struct comedi_async *async;
	struct comedi_async *async;
	struct comedi_subdevice *s;
	struct comedi_subdevice *s;
	int ret = 0;
	int retval = 0;


	if (copy_from_user(&bc, arg, sizeof(struct comedi_bufconfig)))
	if (copy_from_user(&bc, arg, sizeof(struct comedi_bufconfig)))
		return -EFAULT;
		return -EFAULT;
@@ -303,37 +313,9 @@ static int do_bufconfig_ioctl(struct comedi_device *dev, void *arg)
	}
	}


	if (bc.size) {
	if (bc.size) {
		if (bc.size > async->max_bufsize)
		retval = resize_async_buffer(dev, s, async, bc.size);
			return -EPERM;
		if (retval < 0)

			return retval;
		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);
	}
	}


	bc.size = async->prealloc_bufsz;
	bc.size = async->prealloc_bufsz;
@@ -2132,6 +2114,7 @@ int comedi_alloc_board_minor(struct device *hardware_device)
	struct comedi_device_file_info *info;
	struct comedi_device_file_info *info;
	struct device *csdev;
	struct device *csdev;
	unsigned i;
	unsigned i;
	int retval;


	info = kzalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
	info = kzalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
	if (info == NULL)
	if (info == NULL)
@@ -2154,8 +2137,7 @@ int comedi_alloc_board_minor(struct device *hardware_device)
		comedi_device_cleanup(info->device);
		comedi_device_cleanup(info->device);
		kfree(info->device);
		kfree(info->device);
		kfree(info);
		kfree(info);
		rt_printk
		printk(KERN_ERR "comedi: error: ran out of minor numbers for board device files.\n");
		    ("comedi: error: ran out of minor numbers for board device files.\n");
		return -EBUSY;
		return -EBUSY;
	}
	}
	info->device->minor = i;
	info->device->minor = i;
@@ -2164,7 +2146,35 @@ int comedi_alloc_board_minor(struct device *hardware_device)
				     hardware_device, "comedi%i", i);
				     hardware_device, "comedi%i", i);
	if (!IS_ERR(csdev))
	if (!IS_ERR(csdev))
		info->device->class_dev = 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;
	return i;
}
}


@@ -2193,12 +2203,14 @@ void comedi_free_board_minor(unsigned minor)
	}
	}
}
}


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;
	unsigned long flags;
	struct comedi_device_file_info *info;
	struct comedi_device_file_info *info;
	struct device *csdev;
	struct device *csdev;
	unsigned i;
	unsigned i;
	int retval;


	info = kmalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
	info = kmalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
	if (info == NULL)
	if (info == NULL)
@@ -2216,8 +2228,7 @@ int comedi_alloc_subdevice_minor(struct comedi_device *dev, struct comedi_subdev
	comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
	comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
	if (i == COMEDI_NUM_MINORS) {
	if (i == COMEDI_NUM_MINORS) {
		kfree(info);
		kfree(info);
		rt_printk
		printk(KERN_ERR "comedi: error: ran out of minor numbers for board device files.\n");
		    ("comedi: error: ran out of minor numbers for board device files.\n");
		return -EBUSY;
		return -EBUSY;
	}
	}
	s->minor = i;
	s->minor = i;
@@ -2227,7 +2238,35 @@ int comedi_alloc_subdevice_minor(struct comedi_device *dev, struct comedi_subdev
				     (int)(s - dev->subdevices));
				     (int)(s - dev->subdevices));
	if (!IS_ERR(csdev))
	if (!IS_ERR(csdev))
		s->class_dev = 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 i;
}
}


@@ -2267,3 +2306,304 @@ struct comedi_device_file_info *comedi_get_device_file_info(unsigned minor)
	comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
	comedi_spin_unlock_irqrestore(&comedi_file_info_table_lock, flags);
	return info;
	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
};