3703 lines · 87.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* * comedi/comedi_fops.c * comedi kernel module * * COMEDI - Linux Control and Measurement Device Interface * Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org> * compat ioctls: * Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk> * Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/> */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/sched/signal.h>#include <linux/fcntl.h>#include <linux/delay.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/poll.h>#include <linux/device.h>#include <linux/fs.h>#include <linux/comedi/comedidev.h>#include <linux/cdev.h> #include <linux/io.h>#include <linux/uaccess.h>#include <linux/compat.h> #include "comedi_internal.h" /* * comedi_subdevice "runflags" * COMEDI_SRF_RT:		DEPRECATED: command is running real-time * COMEDI_SRF_ERROR:		indicates an COMEDI_CB_ERROR event has occurred *				since the last command was started * COMEDI_SRF_RUNNING:		command is running * COMEDI_SRF_BUSY:		command was started and subdevice still busy * COMEDI_SRF_FREE_SPRIV:	free s->private on detach * * COMEDI_SRF_BUSY_MASK:	runflags that indicate the subdevice is "busy" */#define COMEDI_SRF_RT		BIT(1)#define COMEDI_SRF_ERROR	BIT(2)#define COMEDI_SRF_RUNNING	BIT(27)#define COMEDI_SRF_BUSY		BIT(28)#define COMEDI_SRF_FREE_SPRIV	BIT(31) #define COMEDI_SRF_BUSY_MASK	\	(COMEDI_SRF_ERROR | COMEDI_SRF_RUNNING | COMEDI_SRF_BUSY) /** * struct comedi_file - Per-file private data for COMEDI device * @dev: COMEDI device. * @read_subdev: Current "read" subdevice. * @write_subdev: Current "write" subdevice. * @last_detach_count: Last known detach count. * @last_attached: Last known attached/detached state. */struct comedi_file {	struct comedi_device *dev;	struct comedi_subdevice *read_subdev;	struct comedi_subdevice *write_subdev;	unsigned int last_detach_count;	unsigned int last_attached:1;}; #define COMEDI_NUM_MINORS 0x100#define COMEDI_NUM_SUBDEVICE_MINORS	\	(COMEDI_NUM_MINORS - COMEDI_NUM_BOARD_MINORS) static unsigned short comedi_num_legacy_minors;module_param(comedi_num_legacy_minors, ushort, 0444);MODULE_PARM_DESC(comedi_num_legacy_minors,		 "number of comedi minor devices to reserve for non-auto-configured devices (default 0)"		); unsigned int comedi_default_buf_size_kb = CONFIG_COMEDI_DEFAULT_BUF_SIZE_KB;module_param(comedi_default_buf_size_kb, uint, 0644);MODULE_PARM_DESC(comedi_default_buf_size_kb,		 "default asynchronous buffer size in KiB (default "		 __MODULE_STRING(CONFIG_COMEDI_DEFAULT_BUF_SIZE_KB) ")"); unsigned int comedi_default_buf_maxsize_kb =	CONFIG_COMEDI_DEFAULT_BUF_MAXSIZE_KB;module_param(comedi_default_buf_maxsize_kb, uint, 0644);MODULE_PARM_DESC(comedi_default_buf_maxsize_kb,		 "default maximum size of asynchronous buffer in KiB (default "		 __MODULE_STRING(CONFIG_COMEDI_DEFAULT_BUF_MAXSIZE_KB) ")"); static DEFINE_MUTEX(comedi_board_minor_table_lock);static struct comedi_device*comedi_board_minor_table[COMEDI_NUM_BOARD_MINORS]; static DEFINE_MUTEX(comedi_subdevice_minor_table_lock);/* Note: indexed by minor - COMEDI_NUM_BOARD_MINORS. */static struct comedi_subdevice*comedi_subdevice_minor_table[COMEDI_NUM_SUBDEVICE_MINORS]; static struct cdev comedi_cdev; static void comedi_device_init(struct comedi_device *dev){	kref_init(&dev->refcount);	spin_lock_init(&dev->spinlock);	mutex_init(&dev->mutex);	init_rwsem(&dev->attach_lock);	dev->minor = -1;} static void comedi_dev_kref_release(struct kref *kref){	struct comedi_device *dev =		container_of(kref, struct comedi_device, refcount); 	mutex_destroy(&dev->mutex);	put_device(dev->class_dev);	kfree(dev);} /** * comedi_dev_put() - Release a use of a COMEDI device * @dev: COMEDI device. * * Must be called when a user of a COMEDI device is finished with it. * When the last user of the COMEDI device calls this function, the * COMEDI device is destroyed. * * Return: 1 if the COMEDI device is destroyed by this call or @dev is * NULL, otherwise return 0.  Callers must not assume the COMEDI * device is still valid if this function returns 0. */int comedi_dev_put(struct comedi_device *dev){	if (dev)		return kref_put(&dev->refcount, comedi_dev_kref_release);	return 1;}EXPORT_SYMBOL_GPL(comedi_dev_put); static struct comedi_device *comedi_dev_get(struct comedi_device *dev){	if (dev)		kref_get(&dev->refcount);	return dev;} static void comedi_device_cleanup(struct comedi_device *dev){	struct module *driver_module = NULL; 	if (!dev)		return;	mutex_lock(&dev->mutex);	if (dev->attached)		driver_module = dev->driver->module;	comedi_device_detach(dev);	if (driver_module && dev->use_count)		module_put(driver_module);	mutex_unlock(&dev->mutex);} static bool comedi_clear_board_dev(struct comedi_device *dev){	unsigned int i = dev->minor;	bool cleared = false; 	lockdep_assert_held(&dev->mutex);	mutex_lock(&comedi_board_minor_table_lock);	if (dev == comedi_board_minor_table[i]) {		comedi_board_minor_table[i] = NULL;		cleared = true;	}	mutex_unlock(&comedi_board_minor_table_lock);	return cleared;} static struct comedi_device *comedi_clear_board_minor(unsigned int minor){	struct comedi_device *dev; 	mutex_lock(&comedi_board_minor_table_lock);	dev = comedi_board_minor_table[minor];	comedi_board_minor_table[minor] = NULL;	mutex_unlock(&comedi_board_minor_table_lock);	return dev;} static struct comedi_subdevice *comedi_subdevice_from_minor(const struct comedi_device *dev, unsigned int minor){	struct comedi_subdevice *s;	unsigned int i = minor - COMEDI_NUM_BOARD_MINORS; 	mutex_lock(&comedi_subdevice_minor_table_lock);	s = comedi_subdevice_minor_table[i];	if (s && s->device != dev)		s = NULL;	mutex_unlock(&comedi_subdevice_minor_table_lock);	return s;} static struct comedi_device *comedi_dev_get_from_board_minor(unsigned int minor){	struct comedi_device *dev; 	mutex_lock(&comedi_board_minor_table_lock);	dev = comedi_dev_get(comedi_board_minor_table[minor]);	mutex_unlock(&comedi_board_minor_table_lock);	return dev;} static struct comedi_device *comedi_dev_get_from_subdevice_minor(unsigned int minor){	struct comedi_device *dev;	struct comedi_subdevice *s;	unsigned int i = minor - COMEDI_NUM_BOARD_MINORS; 	mutex_lock(&comedi_subdevice_minor_table_lock);	s = comedi_subdevice_minor_table[i];	dev = comedi_dev_get(s ? s->device : NULL);	mutex_unlock(&comedi_subdevice_minor_table_lock);	return dev;} /** * comedi_dev_get_from_minor() - Get COMEDI device by minor device number * @minor: Minor device number. * * Finds the COMEDI device associated with the minor device number, if any, * and increments its reference count.  The COMEDI device is prevented from * being freed until a matching call is made to comedi_dev_put(). * * Return: A pointer to the COMEDI device if it exists, with its usage * reference incremented.  Return NULL if no COMEDI device exists with the * specified minor device number. */struct comedi_device *comedi_dev_get_from_minor(unsigned int minor){	if (minor < COMEDI_NUM_BOARD_MINORS)		return comedi_dev_get_from_board_minor(minor); 	return comedi_dev_get_from_subdevice_minor(minor);}EXPORT_SYMBOL_GPL(comedi_dev_get_from_minor); static struct comedi_subdevice *comedi_read_subdevice(const struct comedi_device *dev, unsigned int minor){	struct comedi_subdevice *s; 	lockdep_assert_held(&dev->mutex);	if (minor >= COMEDI_NUM_BOARD_MINORS) {		s = comedi_subdevice_from_minor(dev, minor);		if (!s || (s->subdev_flags & SDF_CMD_READ))			return s;	}	return dev->read_subdev;} static struct comedi_subdevice *comedi_write_subdevice(const struct comedi_device *dev, unsigned int minor){	struct comedi_subdevice *s; 	lockdep_assert_held(&dev->mutex);	if (minor >= COMEDI_NUM_BOARD_MINORS) {		s = comedi_subdevice_from_minor(dev, minor);		if (!s || (s->subdev_flags & SDF_CMD_WRITE))			return s;	}	return dev->write_subdev;} static void comedi_file_reset(struct file *file){	struct comedi_file *cfp = file->private_data;	struct comedi_device *dev = cfp->dev;	struct comedi_subdevice *s, *read_s, *write_s;	unsigned int minor = iminor(file_inode(file)); 	read_s = dev->read_subdev;	write_s = dev->write_subdev;	if (minor >= COMEDI_NUM_BOARD_MINORS) {		s = comedi_subdevice_from_minor(dev, minor);		if (!s || s->subdev_flags & SDF_CMD_READ)			read_s = s;		if (!s || s->subdev_flags & SDF_CMD_WRITE)			write_s = s;	}	cfp->last_attached = dev->attached;	cfp->last_detach_count = dev->detach_count;	WRITE_ONCE(cfp->read_subdev, read_s);	WRITE_ONCE(cfp->write_subdev, write_s);} static void comedi_file_check(struct file *file){	struct comedi_file *cfp = file->private_data;	struct comedi_device *dev = cfp->dev; 	if (cfp->last_attached != dev->attached ||	    cfp->last_detach_count != dev->detach_count)		comedi_file_reset(file);} static struct comedi_subdevice *comedi_file_read_subdevice(struct file *file){	struct comedi_file *cfp = file->private_data; 	comedi_file_check(file);	return READ_ONCE(cfp->read_subdev);} static struct comedi_subdevice *comedi_file_write_subdevice(struct file *file){	struct comedi_file *cfp = file->private_data; 	comedi_file_check(file);	return READ_ONCE(cfp->write_subdev);} static int resize_async_buffer(struct comedi_device *dev,			       struct comedi_subdevice *s,			       unsigned int new_size){	struct comedi_async *async = s->async;	int retval; 	lockdep_assert_held(&dev->mutex); 	if (new_size > async->max_bufsize)		return -EPERM; 	if (s->busy) {		dev_dbg(dev->class_dev,			"subdevice is busy, cannot resize buffer\n");		return -EBUSY;	}	if (comedi_buf_is_mmapped(s)) {		dev_dbg(dev->class_dev,			"subdevice is mmapped, cannot resize buffer\n");		return -EBUSY;	} 	/* 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);		if (retval < 0)			return retval;	} 	dev_dbg(dev->class_dev, "subd %d buffer resized to %i bytes\n",		s->index, async->prealloc_bufsz);	return 0;} /* sysfs attribute files */ static ssize_t max_read_buffer_kb_show(struct device *csdev,				       struct device_attribute *attr, char *buf){	unsigned int minor = MINOR(csdev->devt);	struct comedi_device *dev;	struct comedi_subdevice *s;	unsigned int size = 0; 	dev = comedi_dev_get_from_minor(minor);	if (!dev)		return -ENODEV; 	mutex_lock(&dev->mutex);	s = comedi_read_subdevice(dev, minor);	if (s && (s->subdev_flags & SDF_CMD_READ) && s->async)		size = s->async->max_bufsize / 1024;	mutex_unlock(&dev->mutex); 	comedi_dev_put(dev);	return sysfs_emit(buf, "%u\n", size);} static ssize_t max_read_buffer_kb_store(struct device *csdev,					struct device_attribute *attr,					const char *buf, size_t count){	unsigned int minor = MINOR(csdev->devt);	struct comedi_device *dev;	struct comedi_subdevice *s;	unsigned int size;	int err; 	err = kstrtouint(buf, 10, &size);	if (err)		return err;	if (size > (UINT_MAX / 1024))		return -EINVAL;	size *= 1024; 	dev = comedi_dev_get_from_minor(minor);	if (!dev)		return -ENODEV; 	mutex_lock(&dev->mutex);	s = comedi_read_subdevice(dev, minor);	if (s && (s->subdev_flags & SDF_CMD_READ) && s->async)		s->async->max_bufsize = size;	else		err = -EINVAL;	mutex_unlock(&dev->mutex); 	comedi_dev_put(dev);	return err ? err : count;}static DEVICE_ATTR_RW(max_read_buffer_kb); static ssize_t read_buffer_kb_show(struct device *csdev,				   struct device_attribute *attr, char *buf){	unsigned int minor = MINOR(csdev->devt);	struct comedi_device *dev;	struct comedi_subdevice *s;	unsigned int size = 0; 	dev = comedi_dev_get_from_minor(minor);	if (!dev)		return -ENODEV; 	mutex_lock(&dev->mutex);	s = comedi_read_subdevice(dev, minor);	if (s && (s->subdev_flags & SDF_CMD_READ) && s->async)		size = s->async->prealloc_bufsz / 1024;	mutex_unlock(&dev->mutex); 	comedi_dev_put(dev);	return sysfs_emit(buf, "%u\n", size);} static ssize_t read_buffer_kb_store(struct device *csdev,				    struct device_attribute *attr,				    const char *buf, size_t count){	unsigned int minor = MINOR(csdev->devt);	struct comedi_device *dev;	struct comedi_subdevice *s;	unsigned int size;	int err; 	err = kstrtouint(buf, 10, &size);	if (err)		return err;	if (size > (UINT_MAX / 1024))		return -EINVAL;	size *= 1024; 	dev = comedi_dev_get_from_minor(minor);	if (!dev)		return -ENODEV; 	mutex_lock(&dev->mutex);	s = comedi_read_subdevice(dev, minor);	if (s && (s->subdev_flags & SDF_CMD_READ) && s->async)		err = resize_async_buffer(dev, s, size);	else		err = -EINVAL;	mutex_unlock(&dev->mutex); 	comedi_dev_put(dev);	return err ? err : count;}static DEVICE_ATTR_RW(read_buffer_kb); static ssize_t max_write_buffer_kb_show(struct device *csdev,					struct device_attribute *attr,					char *buf){	unsigned int minor = MINOR(csdev->devt);	struct comedi_device *dev;	struct comedi_subdevice *s;	unsigned int size = 0; 	dev = comedi_dev_get_from_minor(minor);	if (!dev)		return -ENODEV; 	mutex_lock(&dev->mutex);	s = comedi_write_subdevice(dev, minor);	if (s && (s->subdev_flags & SDF_CMD_WRITE) && s->async)		size = s->async->max_bufsize / 1024;	mutex_unlock(&dev->mutex); 

Lines 1–500 of 3703.