Commit a5011a26 authored by H Hartley Sweeten's avatar H Hartley Sweeten Committed by Greg Kroah-Hartman

staging: comedi: refactor sysfs files in comedi_fops.c

Refactor the sysfs attributes and functions to remove
the need for the forward declarations and use the
DEVICE_ATTR macro to define them.

Instead of individually creating sysfs device attribute
files, wrap them in an attribute_group and use the
sysfs_create_group function to create them.
Signed-off-by: default avatarH Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Ian Abbott <abbotti@mev.co.uk>
Cc: Mori Hess <fmhess@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 54fe68a8
...@@ -129,15 +129,295 @@ static int do_cancel(struct comedi_device *dev, struct comedi_subdevice *s); ...@@ -129,15 +129,295 @@ 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, static int resize_async_buffer(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_subdevice *s,
struct comedi_async *async, unsigned new_size); 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, (int)(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 int new_max_size_kb;
unsigned int new_max_size;
int ret;
struct comedi_subdevice *const read_subdevice =
comedi_get_read_subdevice(info);
ret = kstrtouint(buf, 10, &new_max_size_kb);
if (ret)
return ret;
if (new_max_size_kb > (UINT_MAX / bytes_per_kibi))
return -EINVAL;
new_max_size = new_max_size_kb * bytes_per_kibi;
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 DEVICE_ATTR(max_read_buffer_kb, S_IRUGO | S_IWUSR,
show_max_read_buffer_kb, 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 int new_size_kb;
unsigned int new_size;
int retval;
int ret;
struct comedi_subdevice *const read_subdevice =
comedi_get_read_subdevice(info);
ret = kstrtouint(buf, 10, &new_size_kb);
if (ret)
return ret;
if (new_size_kb > (UINT_MAX / bytes_per_kibi))
return -EINVAL;
new_size = new_size_kb * bytes_per_kibi;
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;
}
/* declarations for sysfs attribute files */ static DEVICE_ATTR(read_buffer_kb, S_IRUGO | S_IWUSR | S_IWGRP,
static struct device_attribute dev_attr_max_read_buffer_kb; show_read_buffer_kb, store_read_buffer_kb);
static struct device_attribute dev_attr_read_buffer_kb;
static struct device_attribute dev_attr_max_write_buffer_kb; static ssize_t show_max_write_buffer_kb(struct device *dev,
static struct device_attribute dev_attr_write_buffer_kb; 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 int new_max_size_kb;
unsigned int new_max_size;
int ret;
struct comedi_subdevice *const write_subdevice =
comedi_get_write_subdevice(info);
ret = kstrtouint(buf, 10, &new_max_size_kb);
if (ret)
return ret;
if (new_max_size_kb > (UINT_MAX / bytes_per_kibi))
return -EINVAL;
new_max_size = new_max_size_kb * bytes_per_kibi;
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 DEVICE_ATTR(max_write_buffer_kb, S_IRUGO | S_IWUSR,
show_max_write_buffer_kb, 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 int new_size_kb;
unsigned int new_size;
int retval;
int ret;
struct comedi_subdevice *const write_subdevice =
comedi_get_write_subdevice(info);
ret = kstrtouint(buf, 10, &new_size_kb);
if (ret)
return ret;
if (new_size_kb > (UINT_MAX / bytes_per_kibi))
return -EINVAL;
new_size = ((uint64_t) new_size_kb) * bytes_per_kibi;
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 DEVICE_ATTR(write_buffer_kb, S_IRUGO | S_IWUSR | S_IWGRP,
show_write_buffer_kb, store_write_buffer_kb);
static struct attribute *comedi_attrs[] = {
&dev_attr_max_read_buffer_kb.attr,
&dev_attr_read_buffer_kb.attr,
&dev_attr_max_write_buffer_kb.attr,
&dev_attr_write_buffer_kb.attr,
NULL
};
static const struct attribute_group comedi_sysfs_files = {
.attrs = comedi_attrs,
};
static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd, static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
unsigned long arg) unsigned long arg)
...@@ -1937,804 +2217,453 @@ static int comedi_open(struct inode *inode, struct file *file) ...@@ -1937,804 +2217,453 @@ static int comedi_open(struct inode *inode, struct file *file)
dev->use_count++; dev->use_count++;
mutex_unlock(&dev->mutex); mutex_unlock(&dev->mutex);
return 0;
}
static int comedi_close(struct inode *inode, struct file *file)
{
const unsigned minor = iminor(inode);
struct comedi_subdevice *s = NULL;
int i;
struct comedi_device_file_info *dev_file_info;
struct comedi_device *dev;
dev_file_info = comedi_get_device_file_info(minor);
if (dev_file_info == NULL)
return -ENODEV;
dev = dev_file_info->device;
if (dev == NULL)
return -ENODEV;
mutex_lock(&dev->mutex);
if (dev->subdevices) {
for (i = 0; i < dev->n_subdevices; i++) {
s = dev->subdevices + i;
if (s->busy == file)
do_cancel(dev, s);
if (s->lock == file)
s->lock = NULL;
}
}
if (dev->attached && dev->use_count == 1 && dev->close)
dev->close(dev);
module_put(THIS_MODULE);
if (dev->attached)
module_put(dev->driver->module);
dev->use_count--;
mutex_unlock(&dev->mutex);
if (file->f_flags & FASYNC)
comedi_fasync(-1, file, 0);
return 0;
}
static int comedi_fasync(int fd, struct file *file, int on)
{
const unsigned minor = iminor(file->f_dentry->d_inode);
struct comedi_device_file_info *dev_file_info;
struct comedi_device *dev;
dev_file_info = comedi_get_device_file_info(minor);
if (dev_file_info == NULL)
return -ENODEV;
dev = dev_file_info->device;
if (dev == NULL)
return -ENODEV;
return fasync_helper(fd, file, on, &dev->async_queue);
}
const struct file_operations comedi_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = comedi_unlocked_ioctl,
.compat_ioctl = comedi_compat_ioctl,
.open = comedi_open,
.release = comedi_close,
.read = comedi_read,
.write = comedi_write,
.mmap = comedi_mmap,
.poll = comedi_poll,
.fasync = comedi_fasync,
.llseek = noop_llseek,
};
struct class *comedi_class;
static struct cdev comedi_cdev;
static void comedi_cleanup_legacy_minors(void)
{
unsigned i;
for (i = 0; i < comedi_num_legacy_minors; i++)
comedi_free_board_minor(i);
}
static int __init comedi_init(void)
{
int i;
int retval;
printk(KERN_INFO "comedi: version " COMEDI_RELEASE
" - http://www.comedi.org\n");
if (comedi_num_legacy_minors < 0 ||
comedi_num_legacy_minors > COMEDI_NUM_BOARD_MINORS) {
printk(KERN_ERR "comedi: error: invalid value for module "
"parameter \"comedi_num_legacy_minors\". Valid values "
"are 0 through %i.\n", COMEDI_NUM_BOARD_MINORS);
return -EINVAL;
}
/*
* comedi is unusable if both comedi_autoconfig and
* comedi_num_legacy_minors are zero, so we might as well adjust the
* defaults in that case
*/
if (comedi_autoconfig == 0 && comedi_num_legacy_minors == 0)
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),
COMEDI_NUM_MINORS, "comedi");
if (retval)
return -EIO;
cdev_init(&comedi_cdev, &comedi_fops);
comedi_cdev.owner = THIS_MODULE;
kobject_set_name(&comedi_cdev.kobj, "comedi");
if (cdev_add(&comedi_cdev, MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS)) {
unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
COMEDI_NUM_MINORS);
return -EIO;
}
comedi_class = class_create(THIS_MODULE, "comedi");
if (IS_ERR(comedi_class)) {
printk(KERN_ERR "comedi: failed to create class");
cdev_del(&comedi_cdev);
unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
COMEDI_NUM_MINORS);
return PTR_ERR(comedi_class);
}
/* XXX requires /proc interface */
comedi_proc_init();
/* create devices files for legacy/manual use */
for (i = 0; i < comedi_num_legacy_minors; i++) {
int minor;
minor = comedi_alloc_board_minor(NULL);
if (minor < 0) {
comedi_cleanup_legacy_minors();
cdev_del(&comedi_cdev);
unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
COMEDI_NUM_MINORS);
return minor;
}
}
return 0;
}
static void __exit comedi_cleanup(void)
{
int i;
comedi_cleanup_legacy_minors();
for (i = 0; i < COMEDI_NUM_MINORS; ++i)
BUG_ON(comedi_file_info_table[i]);
class_destroy(comedi_class);
cdev_del(&comedi_cdev);
unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS);
comedi_proc_cleanup();
}
module_init(comedi_init);
module_exit(comedi_cleanup);
void comedi_error(const struct comedi_device *dev, const char *s)
{
printk(KERN_ERR "comedi%d: %s: %s\n", dev->minor,
dev->driver->driver_name, s);
}
EXPORT_SYMBOL(comedi_error);
void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s)
{
struct comedi_async *async = s->async;
unsigned runflags = 0;
unsigned runflags_mask = 0;
/* DPRINTK("comedi_event 0x%x\n",mask); */
if ((comedi_get_subdevice_runflags(s) & SRF_RUNNING) == 0)
return;
if (s->
async->events & (COMEDI_CB_EOA | COMEDI_CB_ERROR |
COMEDI_CB_OVERFLOW)) {
runflags_mask |= SRF_RUNNING;
}
/* remember if an error event has occurred, so an error
* can be returned the next time the user does a read() */
if (s->async->events & (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) {
runflags_mask |= SRF_ERROR;
runflags |= SRF_ERROR;
}
if (runflags_mask) {
/*sets SRF_ERROR and SRF_RUNNING together atomically */
comedi_set_subdevice_runflags(s, runflags_mask, runflags);
}
if (async->cb_mask & s->async->events) {
if (comedi_get_subdevice_runflags(s) & SRF_USER) {
wake_up_interruptible(&async->wait_head);
if (s->subdev_flags & SDF_CMD_READ)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
if (s->subdev_flags & SDF_CMD_WRITE)
kill_fasync(&dev->async_queue, SIGIO, POLL_OUT);
} else {
if (async->cb_func)
async->cb_func(s->async->events, async->cb_arg);
}
}
s->async->events = 0;
}
EXPORT_SYMBOL(comedi_event);
unsigned comedi_get_subdevice_runflags(struct comedi_subdevice *s)
{
unsigned long flags;
unsigned runflags;
spin_lock_irqsave(&s->spin_lock, flags); return 0;
runflags = s->runflags;
spin_unlock_irqrestore(&s->spin_lock, flags);
return runflags;
} }
EXPORT_SYMBOL(comedi_get_subdevice_runflags);
static int is_device_busy(struct comedi_device *dev) static int comedi_close(struct inode *inode, struct file *file)
{ {
struct comedi_subdevice *s; const unsigned minor = iminor(inode);
struct comedi_subdevice *s = NULL;
int i; int i;
struct comedi_device_file_info *dev_file_info;
struct comedi_device *dev;
dev_file_info = comedi_get_device_file_info(minor);
if (!dev->attached) if (dev_file_info == NULL)
return 0; return -ENODEV;
dev = dev_file_info->device;
if (dev == NULL)
return -ENODEV;
for (i = 0; i < dev->n_subdevices; i++) { mutex_lock(&dev->mutex);
s = dev->subdevices + i;
if (s->busy) if (dev->subdevices) {
return 1; for (i = 0; i < dev->n_subdevices; i++) {
if (s->async && s->async->mmap_count) s = dev->subdevices + i;
return 1;
if (s->busy == file)
do_cancel(dev, s);
if (s->lock == file)
s->lock = NULL;
}
} }
if (dev->attached && dev->use_count == 1 && dev->close)
dev->close(dev);
module_put(THIS_MODULE);
if (dev->attached)
module_put(dev->driver->module);
dev->use_count--;
mutex_unlock(&dev->mutex);
if (file->f_flags & FASYNC)
comedi_fasync(-1, file, 0);
return 0; return 0;
} }
static void comedi_device_init(struct comedi_device *dev) static int comedi_fasync(int fd, struct file *file, int on)
{ {
memset(dev, 0, sizeof(struct comedi_device)); const unsigned minor = iminor(file->f_dentry->d_inode);
spin_lock_init(&dev->spinlock); struct comedi_device_file_info *dev_file_info;
mutex_init(&dev->mutex); struct comedi_device *dev;
dev->minor = -1; dev_file_info = comedi_get_device_file_info(minor);
}
static void comedi_device_cleanup(struct comedi_device *dev) if (dev_file_info == NULL)
{ return -ENODEV;
dev = dev_file_info->device;
if (dev == NULL) if (dev == NULL)
return; return -ENODEV;
mutex_lock(&dev->mutex);
comedi_device_detach(dev); return fasync_helper(fd, file, on, &dev->async_queue);
mutex_unlock(&dev->mutex);
mutex_destroy(&dev->mutex);
} }
int comedi_alloc_board_minor(struct device *hardware_device) const struct file_operations comedi_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = comedi_unlocked_ioctl,
.compat_ioctl = comedi_compat_ioctl,
.open = comedi_open,
.release = comedi_close,
.read = comedi_read,
.write = comedi_write,
.mmap = comedi_mmap,
.poll = comedi_poll,
.fasync = comedi_fasync,
.llseek = noop_llseek,
};
struct class *comedi_class;
static struct cdev comedi_cdev;
static void comedi_cleanup_legacy_minors(void)
{ {
struct comedi_device_file_info *info;
struct device *csdev;
unsigned i; unsigned i;
int retval;
info = kzalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL); for (i = 0; i < comedi_num_legacy_minors; i++)
if (info == NULL)
return -ENOMEM;
info->device = kzalloc(sizeof(struct comedi_device), GFP_KERNEL);
if (info->device == NULL) {
kfree(info);
return -ENOMEM;
}
info->hardware_device = hardware_device;
comedi_device_init(info->device);
spin_lock(&comedi_file_info_table_lock);
for (i = 0; i < COMEDI_NUM_BOARD_MINORS; ++i) {
if (comedi_file_info_table[i] == NULL) {
comedi_file_info_table[i] = info;
break;
}
}
spin_unlock(&comedi_file_info_table_lock);
if (i == COMEDI_NUM_BOARD_MINORS) {
comedi_device_cleanup(info->device);
kfree(info->device);
kfree(info);
printk(KERN_ERR
"comedi: error: "
"ran out of minor numbers for board device files.\n");
return -EBUSY;
}
info->device->minor = i;
csdev = device_create(comedi_class, hardware_device,
MKDEV(COMEDI_MAJOR, i), NULL, "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); comedi_free_board_minor(i);
return retval;
}
return i;
} }
void comedi_free_board_minor(unsigned minor) static int __init comedi_init(void)
{ {
struct comedi_device_file_info *info; int i;
int retval;
BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS); printk(KERN_INFO "comedi: version " COMEDI_RELEASE
spin_lock(&comedi_file_info_table_lock); " - http://www.comedi.org\n");
info = comedi_file_info_table[minor];
comedi_file_info_table[minor] = NULL;
spin_unlock(&comedi_file_info_table_lock);
if (info) { if (comedi_num_legacy_minors < 0 ||
struct comedi_device *dev = info->device; comedi_num_legacy_minors > COMEDI_NUM_BOARD_MINORS) {
if (dev) { printk(KERN_ERR "comedi: error: invalid value for module "
if (dev->class_dev) { "parameter \"comedi_num_legacy_minors\". Valid values "
device_destroy(comedi_class, "are 0 through %i.\n", COMEDI_NUM_BOARD_MINORS);
MKDEV(COMEDI_MAJOR, dev->minor)); return -EINVAL;
}
comedi_device_cleanup(dev);
kfree(dev);
}
kfree(info);
} }
}
int comedi_find_board_minor(struct device *hardware_device)
{
int minor;
struct comedi_device_file_info *info;
for (minor = 0; minor < COMEDI_NUM_BOARD_MINORS; minor++) { /*
spin_lock(&comedi_file_info_table_lock); * comedi is unusable if both comedi_autoconfig and
info = comedi_file_info_table[minor]; * comedi_num_legacy_minors are zero, so we might as well adjust the
if (info && info->hardware_device == hardware_device) { * defaults in that case
spin_unlock(&comedi_file_info_table_lock); */
return minor; if (comedi_autoconfig == 0 && comedi_num_legacy_minors == 0)
} comedi_num_legacy_minors = 16;
spin_unlock(&comedi_file_info_table_lock);
}
return -ENODEV;
}
int comedi_alloc_subdevice_minor(struct comedi_device *dev, memset(comedi_file_info_table, 0,
struct comedi_subdevice *s) sizeof(struct comedi_device_file_info *) * COMEDI_NUM_MINORS);
{
struct comedi_device_file_info *info;
struct device *csdev;
unsigned i;
int retval;
info = kmalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL); retval = register_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
if (info == NULL) COMEDI_NUM_MINORS, "comedi");
return -ENOMEM; if (retval)
info->device = dev; return -EIO;
info->read_subdevice = s; cdev_init(&comedi_cdev, &comedi_fops);
info->write_subdevice = s; comedi_cdev.owner = THIS_MODULE;
spin_lock(&comedi_file_info_table_lock); kobject_set_name(&comedi_cdev.kobj, "comedi");
for (i = COMEDI_FIRST_SUBDEVICE_MINOR; i < COMEDI_NUM_MINORS; ++i) { if (cdev_add(&comedi_cdev, MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS)) {
if (comedi_file_info_table[i] == NULL) { unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
comedi_file_info_table[i] = info; COMEDI_NUM_MINORS);
break; return -EIO;
}
}
spin_unlock(&comedi_file_info_table_lock);
if (i == COMEDI_NUM_MINORS) {
kfree(info);
printk(KERN_ERR
"comedi: error: "
"ran out of minor numbers for board device files.\n");
return -EBUSY;
}
s->minor = i;
csdev = device_create(comedi_class, dev->class_dev,
MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i_subd%i",
dev->minor, (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); comedi_class = class_create(THIS_MODULE, "comedi");
if (retval) { if (IS_ERR(comedi_class)) {
printk(KERN_ERR printk(KERN_ERR "comedi: failed to create class");
"comedi: " cdev_del(&comedi_cdev);
"failed to create sysfs attribute file \"%s\".\n", unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
dev_attr_max_write_buffer_kb.attr.name); COMEDI_NUM_MINORS);
comedi_free_subdevice_minor(s); return PTR_ERR(comedi_class);
return retval;
} }
retval = device_create_file(csdev, &dev_attr_write_buffer_kb);
if (retval) { /* XXX requires /proc interface */
printk(KERN_ERR comedi_proc_init();
"comedi: "
"failed to create sysfs attribute file \"%s\".\n", /* create devices files for legacy/manual use */
dev_attr_write_buffer_kb.attr.name); for (i = 0; i < comedi_num_legacy_minors; i++) {
comedi_free_subdevice_minor(s); int minor;
return retval; minor = comedi_alloc_board_minor(NULL);
if (minor < 0) {
comedi_cleanup_legacy_minors();
cdev_del(&comedi_cdev);
unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
COMEDI_NUM_MINORS);
return minor;
}
} }
return i;
return 0;
} }
void comedi_free_subdevice_minor(struct comedi_subdevice *s) static void __exit comedi_cleanup(void)
{ {
struct comedi_device_file_info *info; int i;
if (s == NULL)
return;
if (s->minor < 0)
return;
BUG_ON(s->minor >= COMEDI_NUM_MINORS); comedi_cleanup_legacy_minors();
BUG_ON(s->minor < COMEDI_FIRST_SUBDEVICE_MINOR); for (i = 0; i < COMEDI_NUM_MINORS; ++i)
BUG_ON(comedi_file_info_table[i]);
spin_lock(&comedi_file_info_table_lock); class_destroy(comedi_class);
info = comedi_file_info_table[s->minor]; cdev_del(&comedi_cdev);
comedi_file_info_table[s->minor] = NULL; unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS);
spin_unlock(&comedi_file_info_table_lock);
if (s->class_dev) { comedi_proc_cleanup();
device_destroy(comedi_class, MKDEV(COMEDI_MAJOR, s->minor));
s->class_dev = NULL;
}
kfree(info);
} }
struct comedi_device_file_info *comedi_get_device_file_info(unsigned minor) module_init(comedi_init);
{ module_exit(comedi_cleanup);
struct comedi_device_file_info *info;
BUG_ON(minor >= COMEDI_NUM_MINORS); void comedi_error(const struct comedi_device *dev, const char *s)
spin_lock(&comedi_file_info_table_lock); {
info = comedi_file_info_table[minor]; printk(KERN_ERR "comedi%d: %s: %s\n", dev->minor,
spin_unlock(&comedi_file_info_table_lock); dev->driver->driver_name, s);
return info;
} }
EXPORT_SYMBOL_GPL(comedi_get_device_file_info); EXPORT_SYMBOL(comedi_error);
static int resize_async_buffer(struct comedi_device *dev, void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s)
struct comedi_subdevice *s,
struct comedi_async *async, unsigned new_size)
{ {
int retval; struct comedi_async *async = s->async;
unsigned runflags = 0;
unsigned runflags_mask = 0;
if (new_size > async->max_bufsize) /* DPRINTK("comedi_event 0x%x\n",mask); */
return -EPERM;
if (s->busy) { if ((comedi_get_subdevice_runflags(s) & SRF_RUNNING) == 0)
DPRINTK("subdevice is busy, cannot resize buffer\n"); return;
return -EBUSY;
if (s->
async->events & (COMEDI_CB_EOA | COMEDI_CB_ERROR |
COMEDI_CB_OVERFLOW)) {
runflags_mask |= SRF_RUNNING;
} }
if (async->mmap_count) { /* remember if an error event has occurred, so an error
DPRINTK("subdevice is mmapped, cannot resize buffer\n"); * can be returned the next time the user does a read() */
return -EBUSY; if (s->async->events & (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW)) {
runflags_mask |= SRF_ERROR;
runflags |= SRF_ERROR;
} }
if (runflags_mask) {
if (!async->prealloc_buf) /*sets SRF_ERROR and SRF_RUNNING together atomically */
return -EINVAL; comedi_set_subdevice_runflags(s, runflags_mask, runflags);
/* 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", if (async->cb_mask & s->async->events) {
dev->minor, (int)(s - dev->subdevices), async->prealloc_bufsz); if (comedi_get_subdevice_runflags(s) & SRF_USER) {
return 0; wake_up_interruptible(&async->wait_head);
if (s->subdev_flags & SDF_CMD_READ)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
if (s->subdev_flags & SDF_CMD_WRITE)
kill_fasync(&dev->async_queue, SIGIO, POLL_OUT);
} else {
if (async->cb_func)
async->cb_func(s->async->events, async->cb_arg);
}
}
s->async->events = 0;
} }
EXPORT_SYMBOL(comedi_event);
/* sysfs attribute files */ unsigned comedi_get_subdevice_runflags(struct comedi_subdevice *s)
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; unsigned long flags;
struct comedi_device_file_info *info = dev_get_drvdata(dev); unsigned runflags;
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; spin_lock_irqsave(&s->spin_lock, flags);
runflags = s->runflags;
spin_unlock_irqrestore(&s->spin_lock, flags);
return runflags;
} }
EXPORT_SYMBOL(comedi_get_subdevice_runflags);
static ssize_t store_max_read_buffer_kb(struct device *dev, static int is_device_busy(struct comedi_device *dev)
struct device_attribute *attr,
const char *buf, size_t count)
{ {
struct comedi_device_file_info *info = dev_get_drvdata(dev); struct comedi_subdevice *s;
unsigned int new_max_size_kb; int i;
unsigned int new_max_size;
int ret;
struct comedi_subdevice *const read_subdevice =
comedi_get_read_subdevice(info);
ret = kstrtouint(buf, 10, &new_max_size_kb); if (!dev->attached)
if (ret) return 0;
return ret;
if (new_max_size_kb > (UINT_MAX / bytes_per_kibi))
return -EINVAL;
new_max_size = new_max_size_kb * bytes_per_kibi;
mutex_lock(&info->device->mutex); for (i = 0; i < dev->n_subdevices; i++) {
if (read_subdevice == NULL || s = dev->subdevices + i;
(read_subdevice->subdev_flags & SDF_CMD_READ) == 0 || if (s->busy)
read_subdevice->async == NULL) { return 1;
mutex_unlock(&info->device->mutex); if (s->async && s->async->mmap_count)
return -EINVAL; return 1;
} }
read_subdevice->async->max_bufsize = new_max_size;
mutex_unlock(&info->device->mutex);
return count; return 0;
} }
static struct device_attribute dev_attr_max_read_buffer_kb = { static void comedi_device_init(struct comedi_device *dev)
.attr = { {
.name = "max_read_buffer_kb", memset(dev, 0, sizeof(struct comedi_device));
.mode = S_IRUGO | S_IWUSR}, spin_lock_init(&dev->spinlock);
.show = &show_max_read_buffer_kb, mutex_init(&dev->mutex);
.store = &store_max_read_buffer_kb dev->minor = -1;
}; }
static ssize_t show_read_buffer_kb(struct device *dev, static void comedi_device_cleanup(struct comedi_device *dev)
struct device_attribute *attr, char *buf)
{ {
ssize_t retval; if (dev == NULL)
struct comedi_device_file_info *info = dev_get_drvdata(dev); return;
unsigned buffer_size_kb = 0; mutex_lock(&dev->mutex);
struct comedi_subdevice *const read_subdevice = comedi_device_detach(dev);
comedi_get_read_subdevice(info); mutex_unlock(&dev->mutex);
mutex_destroy(&dev->mutex);
}
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)
return -ENOMEM;
info->device = kzalloc(sizeof(struct comedi_device), GFP_KERNEL);
if (info->device == NULL) {
kfree(info);
return -ENOMEM;
}
info->hardware_device = hardware_device;
comedi_device_init(info->device);
spin_lock(&comedi_file_info_table_lock);
for (i = 0; i < COMEDI_NUM_BOARD_MINORS; ++i) {
if (comedi_file_info_table[i] == NULL) {
comedi_file_info_table[i] = info;
break;
}
}
spin_unlock(&comedi_file_info_table_lock);
if (i == COMEDI_NUM_BOARD_MINORS) {
comedi_device_cleanup(info->device);
kfree(info->device);
kfree(info);
printk(KERN_ERR
"comedi: error: "
"ran out of minor numbers for board device files.\n");
return -EBUSY;
}
info->device->minor = i;
csdev = device_create(comedi_class, hardware_device,
MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i", i);
if (!IS_ERR(csdev))
info->device->class_dev = csdev;
dev_set_drvdata(csdev, info);
mutex_lock(&info->device->mutex); retval = sysfs_create_group(&csdev->kobj, &comedi_sysfs_files);
if (read_subdevice && if (retval) {
(read_subdevice->subdev_flags & SDF_CMD_READ) && printk(KERN_ERR
read_subdevice->async) { "comedi: failed to create sysfs attribute files\n");
buffer_size_kb = read_subdevice->async->prealloc_bufsz / comedi_free_board_minor(i);
bytes_per_kibi; return retval;
} }
retval = snprintf(buf, PAGE_SIZE, "%i\n", buffer_size_kb);
mutex_unlock(&info->device->mutex);
return retval; return i;
} }
static ssize_t store_read_buffer_kb(struct device *dev, void comedi_free_board_minor(unsigned minor)
struct device_attribute *attr,
const char *buf, size_t count)
{ {
struct comedi_device_file_info *info = dev_get_drvdata(dev); struct comedi_device_file_info *info;
unsigned int new_size_kb;
unsigned int new_size;
int retval;
int ret;
struct comedi_subdevice *const read_subdevice =
comedi_get_read_subdevice(info);
ret = kstrtouint(buf, 10, &new_size_kb); BUG_ON(minor >= COMEDI_NUM_BOARD_MINORS);
if (ret) spin_lock(&comedi_file_info_table_lock);
return ret; info = comedi_file_info_table[minor];
if (new_size_kb > (UINT_MAX / bytes_per_kibi)) comedi_file_info_table[minor] = NULL;
return -EINVAL; spin_unlock(&comedi_file_info_table_lock);
new_size = new_size_kb * bytes_per_kibi;
mutex_lock(&info->device->mutex); if (info) {
if (read_subdevice == NULL || struct comedi_device *dev = info->device;
(read_subdevice->subdev_flags & SDF_CMD_READ) == 0 || if (dev) {
read_subdevice->async == NULL) { if (dev->class_dev) {
mutex_unlock(&info->device->mutex); device_destroy(comedi_class,
return -EINVAL; MKDEV(COMEDI_MAJOR, dev->minor));
}
comedi_device_cleanup(dev);
kfree(dev);
}
kfree(info);
} }
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 = { int comedi_find_board_minor(struct device *hardware_device)
.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; int minor;
struct comedi_device_file_info *info = dev_get_drvdata(dev); struct comedi_device_file_info *info;
unsigned max_buffer_size_kb = 0;
struct comedi_subdevice *const write_subdevice =
comedi_get_write_subdevice(info);
mutex_lock(&info->device->mutex); for (minor = 0; minor < COMEDI_NUM_BOARD_MINORS; minor++) {
if (write_subdevice && spin_lock(&comedi_file_info_table_lock);
(write_subdevice->subdev_flags & SDF_CMD_WRITE) && info = comedi_file_info_table[minor];
write_subdevice->async) { if (info && info->hardware_device == hardware_device) {
max_buffer_size_kb = write_subdevice->async->max_bufsize / spin_unlock(&comedi_file_info_table_lock);
bytes_per_kibi; return minor;
}
spin_unlock(&comedi_file_info_table_lock);
} }
retval = snprintf(buf, PAGE_SIZE, "%i\n", max_buffer_size_kb); return -ENODEV;
mutex_unlock(&info->device->mutex);
return retval;
} }
static ssize_t store_max_write_buffer_kb(struct device *dev, int comedi_alloc_subdevice_minor(struct comedi_device *dev,
struct device_attribute *attr, struct comedi_subdevice *s)
const char *buf, size_t count)
{ {
struct comedi_device_file_info *info = dev_get_drvdata(dev); struct comedi_device_file_info *info;
unsigned int new_max_size_kb; struct device *csdev;
unsigned int new_max_size; unsigned i;
int ret; int retval;
struct comedi_subdevice *const write_subdevice =
comedi_get_write_subdevice(info);
ret = kstrtouint(buf, 10, &new_max_size_kb); info = kmalloc(sizeof(struct comedi_device_file_info), GFP_KERNEL);
if (ret) if (info == NULL)
return ret; return -ENOMEM;
if (new_max_size_kb > (UINT_MAX / bytes_per_kibi)) info->device = dev;
return -EINVAL; info->read_subdevice = s;
new_max_size = new_max_size_kb * bytes_per_kibi; info->write_subdevice = s;
spin_lock(&comedi_file_info_table_lock);
for (i = COMEDI_FIRST_SUBDEVICE_MINOR; i < COMEDI_NUM_MINORS; ++i) {
if (comedi_file_info_table[i] == NULL) {
comedi_file_info_table[i] = info;
break;
}
}
spin_unlock(&comedi_file_info_table_lock);
if (i == COMEDI_NUM_MINORS) {
kfree(info);
printk(KERN_ERR
"comedi: error: "
"ran out of minor numbers for board device files.\n");
return -EBUSY;
}
s->minor = i;
csdev = device_create(comedi_class, dev->class_dev,
MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i_subd%i",
dev->minor, (int)(s - dev->subdevices));
if (!IS_ERR(csdev))
s->class_dev = csdev;
dev_set_drvdata(csdev, info);
mutex_lock(&info->device->mutex); retval = sysfs_create_group(&csdev->kobj, &comedi_sysfs_files);
if (write_subdevice == NULL || if (retval) {
(write_subdevice->subdev_flags & SDF_CMD_WRITE) == 0 || printk(KERN_ERR
write_subdevice->async == NULL) { "comedi: failed to create sysfs attribute files\n");
mutex_unlock(&info->device->mutex); comedi_free_subdevice_minor(s);
return -EINVAL; return retval;
} }
write_subdevice->async->max_bufsize = new_max_size;
mutex_unlock(&info->device->mutex);
return count; return i;
} }
static struct device_attribute dev_attr_max_write_buffer_kb = { void comedi_free_subdevice_minor(struct comedi_subdevice *s)
.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;
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; if (s == NULL)
} return;
if (s->minor < 0)
return;
static ssize_t store_write_buffer_kb(struct device *dev, BUG_ON(s->minor >= COMEDI_NUM_MINORS);
struct device_attribute *attr, BUG_ON(s->minor < COMEDI_FIRST_SUBDEVICE_MINOR);
const char *buf, size_t count)
{
struct comedi_device_file_info *info = dev_get_drvdata(dev);
unsigned int new_size_kb;
unsigned int new_size;
int retval;
int ret;
struct comedi_subdevice *const write_subdevice =
comedi_get_write_subdevice(info);
ret = kstrtouint(buf, 10, &new_size_kb); spin_lock(&comedi_file_info_table_lock);
if (ret) info = comedi_file_info_table[s->minor];
return ret; comedi_file_info_table[s->minor] = NULL;
if (new_size_kb > (UINT_MAX / bytes_per_kibi)) spin_unlock(&comedi_file_info_table_lock);
return -EINVAL;
new_size = ((uint64_t) new_size_kb) * bytes_per_kibi;
mutex_lock(&info->device->mutex); if (s->class_dev) {
if (write_subdevice == NULL || device_destroy(comedi_class, MKDEV(COMEDI_MAJOR, s->minor));
(write_subdevice->subdev_flags & SDF_CMD_WRITE) == 0 || s->class_dev = NULL;
write_subdevice->async == NULL) {
mutex_unlock(&info->device->mutex);
return -EINVAL;
} }
retval = resize_async_buffer(info->device, write_subdevice, kfree(info);
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 = { struct comedi_device_file_info *comedi_get_device_file_info(unsigned minor)
.attr = { {
.name = "write_buffer_kb", struct comedi_device_file_info *info;
.mode = S_IRUGO | S_IWUSR | S_IWGRP},
.show = &show_write_buffer_kb, BUG_ON(minor >= COMEDI_NUM_MINORS);
.store = &store_write_buffer_kb spin_lock(&comedi_file_info_table_lock);
}; info = comedi_file_info_table[minor];
spin_unlock(&comedi_file_info_table_lock);
return info;
}
EXPORT_SYMBOL_GPL(comedi_get_device_file_info);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment