summaryrefslogtreecommitdiffstats
path: root/drivers/staging/comedi/comedi_fops.c
diff options
context:
space:
mode:
authorFrank Mori Hess2009-04-14 17:21:41 +0200
committerGreg Kroah-Hartman2009-06-19 20:00:30 +0200
commit883db3d9bb4d50f05cbb8b5197f6aef10c1231a9 (patch)
tree8c696828a31546c7ce77ae15a493d93d0a6628b8 /drivers/staging/comedi/comedi_fops.c
parentStaging: comedi: amplc_pc236: Corrected documentation. (diff)
downloadkernel-qcow2-linux-883db3d9bb4d50f05cbb8b5197f6aef10c1231a9.tar.gz
kernel-qcow2-linux-883db3d9bb4d50f05cbb8b5197f6aef10c1231a9.tar.xz
kernel-qcow2-linux-883db3d9bb4d50f05cbb8b5197f6aef10c1231a9.zip
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>
Diffstat (limited to 'drivers/staging/comedi/comedi_fops.c')
-rw-r--r--drivers/staging/comedi/comedi_fops.c418
1 files changed, 379 insertions, 39 deletions
diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c
index 19dce2ebfc19..bb4a2897feae 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 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);
+
+/* 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 @@ static int do_bufconfig_ioctl(struct comedi_device *dev, void *arg)
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 @@ static int do_bufconfig_ioctl(struct 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;
@@ -2132,6 +2114,7 @@ int comedi_alloc_board_minor(struct device *hardware_device)
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 @@ 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(KERN_ERR "comedi: error: ran out of minor numbers for board device files.\n");
return -EBUSY;
}
info->device->minor = i;
@@ -2164,7 +2146,35 @@ int comedi_alloc_board_minor(struct device *hardware_device)
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 @@ 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;
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 @@ int comedi_alloc_subdevice_minor(struct comedi_device *dev, struct comedi_subdev
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 comedi_alloc_subdevice_minor(struct comedi_device *dev, struct comedi_subdev
(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 @@ 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(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
+};