162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * comedi/comedi_fops.c 462306a36Sopenharmony_ci * comedi kernel module 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * COMEDI - Linux Control and Measurement Device Interface 762306a36Sopenharmony_ci * Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org> 862306a36Sopenharmony_ci * compat ioctls: 962306a36Sopenharmony_ci * Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk> 1062306a36Sopenharmony_ci * Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/errno.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/sched/signal.h> 1962306a36Sopenharmony_ci#include <linux/fcntl.h> 2062306a36Sopenharmony_ci#include <linux/delay.h> 2162306a36Sopenharmony_ci#include <linux/mm.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/poll.h> 2462306a36Sopenharmony_ci#include <linux/device.h> 2562306a36Sopenharmony_ci#include <linux/fs.h> 2662306a36Sopenharmony_ci#include <linux/comedi/comedidev.h> 2762306a36Sopenharmony_ci#include <linux/cdev.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/io.h> 3062306a36Sopenharmony_ci#include <linux/uaccess.h> 3162306a36Sopenharmony_ci#include <linux/compat.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include "comedi_internal.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * comedi_subdevice "runflags" 3762306a36Sopenharmony_ci * COMEDI_SRF_RT: DEPRECATED: command is running real-time 3862306a36Sopenharmony_ci * COMEDI_SRF_ERROR: indicates an COMEDI_CB_ERROR event has occurred 3962306a36Sopenharmony_ci * since the last command was started 4062306a36Sopenharmony_ci * COMEDI_SRF_RUNNING: command is running 4162306a36Sopenharmony_ci * COMEDI_SRF_FREE_SPRIV: free s->private on detach 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * COMEDI_SRF_BUSY_MASK: runflags that indicate the subdevice is "busy" 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci#define COMEDI_SRF_RT BIT(1) 4662306a36Sopenharmony_ci#define COMEDI_SRF_ERROR BIT(2) 4762306a36Sopenharmony_ci#define COMEDI_SRF_RUNNING BIT(27) 4862306a36Sopenharmony_ci#define COMEDI_SRF_FREE_SPRIV BIT(31) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define COMEDI_SRF_BUSY_MASK (COMEDI_SRF_ERROR | COMEDI_SRF_RUNNING) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/** 5362306a36Sopenharmony_ci * struct comedi_file - Per-file private data for COMEDI device 5462306a36Sopenharmony_ci * @dev: COMEDI device. 5562306a36Sopenharmony_ci * @read_subdev: Current "read" subdevice. 5662306a36Sopenharmony_ci * @write_subdev: Current "write" subdevice. 5762306a36Sopenharmony_ci * @last_detach_count: Last known detach count. 5862306a36Sopenharmony_ci * @last_attached: Last known attached/detached state. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_cistruct comedi_file { 6162306a36Sopenharmony_ci struct comedi_device *dev; 6262306a36Sopenharmony_ci struct comedi_subdevice *read_subdev; 6362306a36Sopenharmony_ci struct comedi_subdevice *write_subdev; 6462306a36Sopenharmony_ci unsigned int last_detach_count; 6562306a36Sopenharmony_ci unsigned int last_attached:1; 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define COMEDI_NUM_MINORS 0x100 6962306a36Sopenharmony_ci#define COMEDI_NUM_SUBDEVICE_MINORS \ 7062306a36Sopenharmony_ci (COMEDI_NUM_MINORS - COMEDI_NUM_BOARD_MINORS) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic unsigned short comedi_num_legacy_minors; 7362306a36Sopenharmony_cimodule_param(comedi_num_legacy_minors, ushort, 0444); 7462306a36Sopenharmony_ciMODULE_PARM_DESC(comedi_num_legacy_minors, 7562306a36Sopenharmony_ci "number of comedi minor devices to reserve for non-auto-configured devices (default 0)" 7662306a36Sopenharmony_ci ); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ciunsigned int comedi_default_buf_size_kb = CONFIG_COMEDI_DEFAULT_BUF_SIZE_KB; 7962306a36Sopenharmony_cimodule_param(comedi_default_buf_size_kb, uint, 0644); 8062306a36Sopenharmony_ciMODULE_PARM_DESC(comedi_default_buf_size_kb, 8162306a36Sopenharmony_ci "default asynchronous buffer size in KiB (default " 8262306a36Sopenharmony_ci __MODULE_STRING(CONFIG_COMEDI_DEFAULT_BUF_SIZE_KB) ")"); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ciunsigned int comedi_default_buf_maxsize_kb = 8562306a36Sopenharmony_ci CONFIG_COMEDI_DEFAULT_BUF_MAXSIZE_KB; 8662306a36Sopenharmony_cimodule_param(comedi_default_buf_maxsize_kb, uint, 0644); 8762306a36Sopenharmony_ciMODULE_PARM_DESC(comedi_default_buf_maxsize_kb, 8862306a36Sopenharmony_ci "default maximum size of asynchronous buffer in KiB (default " 8962306a36Sopenharmony_ci __MODULE_STRING(CONFIG_COMEDI_DEFAULT_BUF_MAXSIZE_KB) ")"); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic DEFINE_MUTEX(comedi_board_minor_table_lock); 9262306a36Sopenharmony_cistatic struct comedi_device 9362306a36Sopenharmony_ci*comedi_board_minor_table[COMEDI_NUM_BOARD_MINORS]; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic DEFINE_MUTEX(comedi_subdevice_minor_table_lock); 9662306a36Sopenharmony_ci/* Note: indexed by minor - COMEDI_NUM_BOARD_MINORS. */ 9762306a36Sopenharmony_cistatic struct comedi_subdevice 9862306a36Sopenharmony_ci*comedi_subdevice_minor_table[COMEDI_NUM_SUBDEVICE_MINORS]; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic struct cdev comedi_cdev; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void comedi_device_init(struct comedi_device *dev) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci kref_init(&dev->refcount); 10562306a36Sopenharmony_ci spin_lock_init(&dev->spinlock); 10662306a36Sopenharmony_ci mutex_init(&dev->mutex); 10762306a36Sopenharmony_ci init_rwsem(&dev->attach_lock); 10862306a36Sopenharmony_ci dev->minor = -1; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void comedi_dev_kref_release(struct kref *kref) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct comedi_device *dev = 11462306a36Sopenharmony_ci container_of(kref, struct comedi_device, refcount); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci mutex_destroy(&dev->mutex); 11762306a36Sopenharmony_ci put_device(dev->class_dev); 11862306a36Sopenharmony_ci kfree(dev); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/** 12262306a36Sopenharmony_ci * comedi_dev_put() - Release a use of a COMEDI device 12362306a36Sopenharmony_ci * @dev: COMEDI device. 12462306a36Sopenharmony_ci * 12562306a36Sopenharmony_ci * Must be called when a user of a COMEDI device is finished with it. 12662306a36Sopenharmony_ci * When the last user of the COMEDI device calls this function, the 12762306a36Sopenharmony_ci * COMEDI device is destroyed. 12862306a36Sopenharmony_ci * 12962306a36Sopenharmony_ci * Return: 1 if the COMEDI device is destroyed by this call or @dev is 13062306a36Sopenharmony_ci * NULL, otherwise return 0. Callers must not assume the COMEDI 13162306a36Sopenharmony_ci * device is still valid if this function returns 0. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ciint comedi_dev_put(struct comedi_device *dev) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci if (dev) 13662306a36Sopenharmony_ci return kref_put(&dev->refcount, comedi_dev_kref_release); 13762306a36Sopenharmony_ci return 1; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_dev_put); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic struct comedi_device *comedi_dev_get(struct comedi_device *dev) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci if (dev) 14462306a36Sopenharmony_ci kref_get(&dev->refcount); 14562306a36Sopenharmony_ci return dev; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void comedi_device_cleanup(struct comedi_device *dev) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct module *driver_module = NULL; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (!dev) 15362306a36Sopenharmony_ci return; 15462306a36Sopenharmony_ci mutex_lock(&dev->mutex); 15562306a36Sopenharmony_ci if (dev->attached) 15662306a36Sopenharmony_ci driver_module = dev->driver->module; 15762306a36Sopenharmony_ci comedi_device_detach(dev); 15862306a36Sopenharmony_ci if (driver_module && dev->use_count) 15962306a36Sopenharmony_ci module_put(driver_module); 16062306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic bool comedi_clear_board_dev(struct comedi_device *dev) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci unsigned int i = dev->minor; 16662306a36Sopenharmony_ci bool cleared = false; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 16962306a36Sopenharmony_ci mutex_lock(&comedi_board_minor_table_lock); 17062306a36Sopenharmony_ci if (dev == comedi_board_minor_table[i]) { 17162306a36Sopenharmony_ci comedi_board_minor_table[i] = NULL; 17262306a36Sopenharmony_ci cleared = true; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci mutex_unlock(&comedi_board_minor_table_lock); 17562306a36Sopenharmony_ci return cleared; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic struct comedi_device *comedi_clear_board_minor(unsigned int minor) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct comedi_device *dev; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci mutex_lock(&comedi_board_minor_table_lock); 18362306a36Sopenharmony_ci dev = comedi_board_minor_table[minor]; 18462306a36Sopenharmony_ci comedi_board_minor_table[minor] = NULL; 18562306a36Sopenharmony_ci mutex_unlock(&comedi_board_minor_table_lock); 18662306a36Sopenharmony_ci return dev; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic struct comedi_subdevice * 19062306a36Sopenharmony_cicomedi_subdevice_from_minor(const struct comedi_device *dev, unsigned int minor) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct comedi_subdevice *s; 19362306a36Sopenharmony_ci unsigned int i = minor - COMEDI_NUM_BOARD_MINORS; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci mutex_lock(&comedi_subdevice_minor_table_lock); 19662306a36Sopenharmony_ci s = comedi_subdevice_minor_table[i]; 19762306a36Sopenharmony_ci if (s && s->device != dev) 19862306a36Sopenharmony_ci s = NULL; 19962306a36Sopenharmony_ci mutex_unlock(&comedi_subdevice_minor_table_lock); 20062306a36Sopenharmony_ci return s; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic struct comedi_device *comedi_dev_get_from_board_minor(unsigned int minor) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct comedi_device *dev; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci mutex_lock(&comedi_board_minor_table_lock); 20862306a36Sopenharmony_ci dev = comedi_dev_get(comedi_board_minor_table[minor]); 20962306a36Sopenharmony_ci mutex_unlock(&comedi_board_minor_table_lock); 21062306a36Sopenharmony_ci return dev; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic struct comedi_device * 21462306a36Sopenharmony_cicomedi_dev_get_from_subdevice_minor(unsigned int minor) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci struct comedi_device *dev; 21762306a36Sopenharmony_ci struct comedi_subdevice *s; 21862306a36Sopenharmony_ci unsigned int i = minor - COMEDI_NUM_BOARD_MINORS; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci mutex_lock(&comedi_subdevice_minor_table_lock); 22162306a36Sopenharmony_ci s = comedi_subdevice_minor_table[i]; 22262306a36Sopenharmony_ci dev = comedi_dev_get(s ? s->device : NULL); 22362306a36Sopenharmony_ci mutex_unlock(&comedi_subdevice_minor_table_lock); 22462306a36Sopenharmony_ci return dev; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci/** 22862306a36Sopenharmony_ci * comedi_dev_get_from_minor() - Get COMEDI device by minor device number 22962306a36Sopenharmony_ci * @minor: Minor device number. 23062306a36Sopenharmony_ci * 23162306a36Sopenharmony_ci * Finds the COMEDI device associated with the minor device number, if any, 23262306a36Sopenharmony_ci * and increments its reference count. The COMEDI device is prevented from 23362306a36Sopenharmony_ci * being freed until a matching call is made to comedi_dev_put(). 23462306a36Sopenharmony_ci * 23562306a36Sopenharmony_ci * Return: A pointer to the COMEDI device if it exists, with its usage 23662306a36Sopenharmony_ci * reference incremented. Return NULL if no COMEDI device exists with the 23762306a36Sopenharmony_ci * specified minor device number. 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_cistruct comedi_device *comedi_dev_get_from_minor(unsigned int minor) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci if (minor < COMEDI_NUM_BOARD_MINORS) 24262306a36Sopenharmony_ci return comedi_dev_get_from_board_minor(minor); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return comedi_dev_get_from_subdevice_minor(minor); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_dev_get_from_minor); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic struct comedi_subdevice * 24962306a36Sopenharmony_cicomedi_read_subdevice(const struct comedi_device *dev, unsigned int minor) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct comedi_subdevice *s; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 25462306a36Sopenharmony_ci if (minor >= COMEDI_NUM_BOARD_MINORS) { 25562306a36Sopenharmony_ci s = comedi_subdevice_from_minor(dev, minor); 25662306a36Sopenharmony_ci if (!s || (s->subdev_flags & SDF_CMD_READ)) 25762306a36Sopenharmony_ci return s; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci return dev->read_subdev; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic struct comedi_subdevice * 26362306a36Sopenharmony_cicomedi_write_subdevice(const struct comedi_device *dev, unsigned int minor) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct comedi_subdevice *s; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 26862306a36Sopenharmony_ci if (minor >= COMEDI_NUM_BOARD_MINORS) { 26962306a36Sopenharmony_ci s = comedi_subdevice_from_minor(dev, minor); 27062306a36Sopenharmony_ci if (!s || (s->subdev_flags & SDF_CMD_WRITE)) 27162306a36Sopenharmony_ci return s; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci return dev->write_subdev; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic void comedi_file_reset(struct file *file) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 27962306a36Sopenharmony_ci struct comedi_device *dev = cfp->dev; 28062306a36Sopenharmony_ci struct comedi_subdevice *s, *read_s, *write_s; 28162306a36Sopenharmony_ci unsigned int minor = iminor(file_inode(file)); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci read_s = dev->read_subdev; 28462306a36Sopenharmony_ci write_s = dev->write_subdev; 28562306a36Sopenharmony_ci if (minor >= COMEDI_NUM_BOARD_MINORS) { 28662306a36Sopenharmony_ci s = comedi_subdevice_from_minor(dev, minor); 28762306a36Sopenharmony_ci if (!s || s->subdev_flags & SDF_CMD_READ) 28862306a36Sopenharmony_ci read_s = s; 28962306a36Sopenharmony_ci if (!s || s->subdev_flags & SDF_CMD_WRITE) 29062306a36Sopenharmony_ci write_s = s; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci cfp->last_attached = dev->attached; 29362306a36Sopenharmony_ci cfp->last_detach_count = dev->detach_count; 29462306a36Sopenharmony_ci WRITE_ONCE(cfp->read_subdev, read_s); 29562306a36Sopenharmony_ci WRITE_ONCE(cfp->write_subdev, write_s); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic void comedi_file_check(struct file *file) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 30162306a36Sopenharmony_ci struct comedi_device *dev = cfp->dev; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (cfp->last_attached != dev->attached || 30462306a36Sopenharmony_ci cfp->last_detach_count != dev->detach_count) 30562306a36Sopenharmony_ci comedi_file_reset(file); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic struct comedi_subdevice *comedi_file_read_subdevice(struct file *file) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci comedi_file_check(file); 31362306a36Sopenharmony_ci return READ_ONCE(cfp->read_subdev); 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic struct comedi_subdevice *comedi_file_write_subdevice(struct file *file) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci comedi_file_check(file); 32162306a36Sopenharmony_ci return READ_ONCE(cfp->write_subdev); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic int resize_async_buffer(struct comedi_device *dev, 32562306a36Sopenharmony_ci struct comedi_subdevice *s, 32662306a36Sopenharmony_ci unsigned int new_size) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct comedi_async *async = s->async; 32962306a36Sopenharmony_ci int retval; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (new_size > async->max_bufsize) 33462306a36Sopenharmony_ci return -EPERM; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (s->busy) { 33762306a36Sopenharmony_ci dev_dbg(dev->class_dev, 33862306a36Sopenharmony_ci "subdevice is busy, cannot resize buffer\n"); 33962306a36Sopenharmony_ci return -EBUSY; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci if (comedi_buf_is_mmapped(s)) { 34262306a36Sopenharmony_ci dev_dbg(dev->class_dev, 34362306a36Sopenharmony_ci "subdevice is mmapped, cannot resize buffer\n"); 34462306a36Sopenharmony_ci return -EBUSY; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* make sure buffer is an integral number of pages (we round up) */ 34862306a36Sopenharmony_ci new_size = (new_size + PAGE_SIZE - 1) & PAGE_MASK; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci retval = comedi_buf_alloc(dev, s, new_size); 35162306a36Sopenharmony_ci if (retval < 0) 35262306a36Sopenharmony_ci return retval; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (s->buf_change) { 35562306a36Sopenharmony_ci retval = s->buf_change(dev, s); 35662306a36Sopenharmony_ci if (retval < 0) 35762306a36Sopenharmony_ci return retval; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci dev_dbg(dev->class_dev, "subd %d buffer resized to %i bytes\n", 36162306a36Sopenharmony_ci s->index, async->prealloc_bufsz); 36262306a36Sopenharmony_ci return 0; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci/* sysfs attribute files */ 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic ssize_t max_read_buffer_kb_show(struct device *csdev, 36862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci unsigned int minor = MINOR(csdev->devt); 37162306a36Sopenharmony_ci struct comedi_device *dev; 37262306a36Sopenharmony_ci struct comedi_subdevice *s; 37362306a36Sopenharmony_ci unsigned int size = 0; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci dev = comedi_dev_get_from_minor(minor); 37662306a36Sopenharmony_ci if (!dev) 37762306a36Sopenharmony_ci return -ENODEV; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci mutex_lock(&dev->mutex); 38062306a36Sopenharmony_ci s = comedi_read_subdevice(dev, minor); 38162306a36Sopenharmony_ci if (s && (s->subdev_flags & SDF_CMD_READ) && s->async) 38262306a36Sopenharmony_ci size = s->async->max_bufsize / 1024; 38362306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci comedi_dev_put(dev); 38662306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", size); 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic ssize_t max_read_buffer_kb_store(struct device *csdev, 39062306a36Sopenharmony_ci struct device_attribute *attr, 39162306a36Sopenharmony_ci const char *buf, size_t count) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci unsigned int minor = MINOR(csdev->devt); 39462306a36Sopenharmony_ci struct comedi_device *dev; 39562306a36Sopenharmony_ci struct comedi_subdevice *s; 39662306a36Sopenharmony_ci unsigned int size; 39762306a36Sopenharmony_ci int err; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci err = kstrtouint(buf, 10, &size); 40062306a36Sopenharmony_ci if (err) 40162306a36Sopenharmony_ci return err; 40262306a36Sopenharmony_ci if (size > (UINT_MAX / 1024)) 40362306a36Sopenharmony_ci return -EINVAL; 40462306a36Sopenharmony_ci size *= 1024; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci dev = comedi_dev_get_from_minor(minor); 40762306a36Sopenharmony_ci if (!dev) 40862306a36Sopenharmony_ci return -ENODEV; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci mutex_lock(&dev->mutex); 41162306a36Sopenharmony_ci s = comedi_read_subdevice(dev, minor); 41262306a36Sopenharmony_ci if (s && (s->subdev_flags & SDF_CMD_READ) && s->async) 41362306a36Sopenharmony_ci s->async->max_bufsize = size; 41462306a36Sopenharmony_ci else 41562306a36Sopenharmony_ci err = -EINVAL; 41662306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci comedi_dev_put(dev); 41962306a36Sopenharmony_ci return err ? err : count; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_cistatic DEVICE_ATTR_RW(max_read_buffer_kb); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic ssize_t read_buffer_kb_show(struct device *csdev, 42462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci unsigned int minor = MINOR(csdev->devt); 42762306a36Sopenharmony_ci struct comedi_device *dev; 42862306a36Sopenharmony_ci struct comedi_subdevice *s; 42962306a36Sopenharmony_ci unsigned int size = 0; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci dev = comedi_dev_get_from_minor(minor); 43262306a36Sopenharmony_ci if (!dev) 43362306a36Sopenharmony_ci return -ENODEV; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci mutex_lock(&dev->mutex); 43662306a36Sopenharmony_ci s = comedi_read_subdevice(dev, minor); 43762306a36Sopenharmony_ci if (s && (s->subdev_flags & SDF_CMD_READ) && s->async) 43862306a36Sopenharmony_ci size = s->async->prealloc_bufsz / 1024; 43962306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci comedi_dev_put(dev); 44262306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", size); 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic ssize_t read_buffer_kb_store(struct device *csdev, 44662306a36Sopenharmony_ci struct device_attribute *attr, 44762306a36Sopenharmony_ci const char *buf, size_t count) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci unsigned int minor = MINOR(csdev->devt); 45062306a36Sopenharmony_ci struct comedi_device *dev; 45162306a36Sopenharmony_ci struct comedi_subdevice *s; 45262306a36Sopenharmony_ci unsigned int size; 45362306a36Sopenharmony_ci int err; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci err = kstrtouint(buf, 10, &size); 45662306a36Sopenharmony_ci if (err) 45762306a36Sopenharmony_ci return err; 45862306a36Sopenharmony_ci if (size > (UINT_MAX / 1024)) 45962306a36Sopenharmony_ci return -EINVAL; 46062306a36Sopenharmony_ci size *= 1024; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci dev = comedi_dev_get_from_minor(minor); 46362306a36Sopenharmony_ci if (!dev) 46462306a36Sopenharmony_ci return -ENODEV; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci mutex_lock(&dev->mutex); 46762306a36Sopenharmony_ci s = comedi_read_subdevice(dev, minor); 46862306a36Sopenharmony_ci if (s && (s->subdev_flags & SDF_CMD_READ) && s->async) 46962306a36Sopenharmony_ci err = resize_async_buffer(dev, s, size); 47062306a36Sopenharmony_ci else 47162306a36Sopenharmony_ci err = -EINVAL; 47262306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci comedi_dev_put(dev); 47562306a36Sopenharmony_ci return err ? err : count; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_cistatic DEVICE_ATTR_RW(read_buffer_kb); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic ssize_t max_write_buffer_kb_show(struct device *csdev, 48062306a36Sopenharmony_ci struct device_attribute *attr, 48162306a36Sopenharmony_ci char *buf) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci unsigned int minor = MINOR(csdev->devt); 48462306a36Sopenharmony_ci struct comedi_device *dev; 48562306a36Sopenharmony_ci struct comedi_subdevice *s; 48662306a36Sopenharmony_ci unsigned int size = 0; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci dev = comedi_dev_get_from_minor(minor); 48962306a36Sopenharmony_ci if (!dev) 49062306a36Sopenharmony_ci return -ENODEV; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci mutex_lock(&dev->mutex); 49362306a36Sopenharmony_ci s = comedi_write_subdevice(dev, minor); 49462306a36Sopenharmony_ci if (s && (s->subdev_flags & SDF_CMD_WRITE) && s->async) 49562306a36Sopenharmony_ci size = s->async->max_bufsize / 1024; 49662306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci comedi_dev_put(dev); 49962306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", size); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic ssize_t max_write_buffer_kb_store(struct device *csdev, 50362306a36Sopenharmony_ci struct device_attribute *attr, 50462306a36Sopenharmony_ci const char *buf, size_t count) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci unsigned int minor = MINOR(csdev->devt); 50762306a36Sopenharmony_ci struct comedi_device *dev; 50862306a36Sopenharmony_ci struct comedi_subdevice *s; 50962306a36Sopenharmony_ci unsigned int size; 51062306a36Sopenharmony_ci int err; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci err = kstrtouint(buf, 10, &size); 51362306a36Sopenharmony_ci if (err) 51462306a36Sopenharmony_ci return err; 51562306a36Sopenharmony_ci if (size > (UINT_MAX / 1024)) 51662306a36Sopenharmony_ci return -EINVAL; 51762306a36Sopenharmony_ci size *= 1024; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci dev = comedi_dev_get_from_minor(minor); 52062306a36Sopenharmony_ci if (!dev) 52162306a36Sopenharmony_ci return -ENODEV; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci mutex_lock(&dev->mutex); 52462306a36Sopenharmony_ci s = comedi_write_subdevice(dev, minor); 52562306a36Sopenharmony_ci if (s && (s->subdev_flags & SDF_CMD_WRITE) && s->async) 52662306a36Sopenharmony_ci s->async->max_bufsize = size; 52762306a36Sopenharmony_ci else 52862306a36Sopenharmony_ci err = -EINVAL; 52962306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci comedi_dev_put(dev); 53262306a36Sopenharmony_ci return err ? err : count; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(max_write_buffer_kb); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic ssize_t write_buffer_kb_show(struct device *csdev, 53762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci unsigned int minor = MINOR(csdev->devt); 54062306a36Sopenharmony_ci struct comedi_device *dev; 54162306a36Sopenharmony_ci struct comedi_subdevice *s; 54262306a36Sopenharmony_ci unsigned int size = 0; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci dev = comedi_dev_get_from_minor(minor); 54562306a36Sopenharmony_ci if (!dev) 54662306a36Sopenharmony_ci return -ENODEV; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci mutex_lock(&dev->mutex); 54962306a36Sopenharmony_ci s = comedi_write_subdevice(dev, minor); 55062306a36Sopenharmony_ci if (s && (s->subdev_flags & SDF_CMD_WRITE) && s->async) 55162306a36Sopenharmony_ci size = s->async->prealloc_bufsz / 1024; 55262306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci comedi_dev_put(dev); 55562306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", size); 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic ssize_t write_buffer_kb_store(struct device *csdev, 55962306a36Sopenharmony_ci struct device_attribute *attr, 56062306a36Sopenharmony_ci const char *buf, size_t count) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci unsigned int minor = MINOR(csdev->devt); 56362306a36Sopenharmony_ci struct comedi_device *dev; 56462306a36Sopenharmony_ci struct comedi_subdevice *s; 56562306a36Sopenharmony_ci unsigned int size; 56662306a36Sopenharmony_ci int err; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci err = kstrtouint(buf, 10, &size); 56962306a36Sopenharmony_ci if (err) 57062306a36Sopenharmony_ci return err; 57162306a36Sopenharmony_ci if (size > (UINT_MAX / 1024)) 57262306a36Sopenharmony_ci return -EINVAL; 57362306a36Sopenharmony_ci size *= 1024; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci dev = comedi_dev_get_from_minor(minor); 57662306a36Sopenharmony_ci if (!dev) 57762306a36Sopenharmony_ci return -ENODEV; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci mutex_lock(&dev->mutex); 58062306a36Sopenharmony_ci s = comedi_write_subdevice(dev, minor); 58162306a36Sopenharmony_ci if (s && (s->subdev_flags & SDF_CMD_WRITE) && s->async) 58262306a36Sopenharmony_ci err = resize_async_buffer(dev, s, size); 58362306a36Sopenharmony_ci else 58462306a36Sopenharmony_ci err = -EINVAL; 58562306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci comedi_dev_put(dev); 58862306a36Sopenharmony_ci return err ? err : count; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_cistatic DEVICE_ATTR_RW(write_buffer_kb); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic struct attribute *comedi_dev_attrs[] = { 59362306a36Sopenharmony_ci &dev_attr_max_read_buffer_kb.attr, 59462306a36Sopenharmony_ci &dev_attr_read_buffer_kb.attr, 59562306a36Sopenharmony_ci &dev_attr_max_write_buffer_kb.attr, 59662306a36Sopenharmony_ci &dev_attr_write_buffer_kb.attr, 59762306a36Sopenharmony_ci NULL, 59862306a36Sopenharmony_ci}; 59962306a36Sopenharmony_ciATTRIBUTE_GROUPS(comedi_dev); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic const struct class comedi_class = { 60262306a36Sopenharmony_ci .name = "comedi", 60362306a36Sopenharmony_ci .dev_groups = comedi_dev_groups, 60462306a36Sopenharmony_ci}; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic void comedi_free_board_dev(struct comedi_device *dev) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci if (dev) { 60962306a36Sopenharmony_ci comedi_device_cleanup(dev); 61062306a36Sopenharmony_ci if (dev->class_dev) { 61162306a36Sopenharmony_ci device_destroy(&comedi_class, 61262306a36Sopenharmony_ci MKDEV(COMEDI_MAJOR, dev->minor)); 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci comedi_dev_put(dev); 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic void __comedi_clear_subdevice_runflags(struct comedi_subdevice *s, 61962306a36Sopenharmony_ci unsigned int bits) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci s->runflags &= ~bits; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic void __comedi_set_subdevice_runflags(struct comedi_subdevice *s, 62562306a36Sopenharmony_ci unsigned int bits) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci s->runflags |= bits; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic void comedi_update_subdevice_runflags(struct comedi_subdevice *s, 63162306a36Sopenharmony_ci unsigned int mask, 63262306a36Sopenharmony_ci unsigned int bits) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci unsigned long flags; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci spin_lock_irqsave(&s->spin_lock, flags); 63762306a36Sopenharmony_ci __comedi_clear_subdevice_runflags(s, mask); 63862306a36Sopenharmony_ci __comedi_set_subdevice_runflags(s, bits & mask); 63962306a36Sopenharmony_ci spin_unlock_irqrestore(&s->spin_lock, flags); 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic unsigned int __comedi_get_subdevice_runflags(struct comedi_subdevice *s) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci return s->runflags; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic unsigned int comedi_get_subdevice_runflags(struct comedi_subdevice *s) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci unsigned long flags; 65062306a36Sopenharmony_ci unsigned int runflags; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci spin_lock_irqsave(&s->spin_lock, flags); 65362306a36Sopenharmony_ci runflags = __comedi_get_subdevice_runflags(s); 65462306a36Sopenharmony_ci spin_unlock_irqrestore(&s->spin_lock, flags); 65562306a36Sopenharmony_ci return runflags; 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic bool comedi_is_runflags_running(unsigned int runflags) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci return runflags & COMEDI_SRF_RUNNING; 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic bool comedi_is_runflags_in_error(unsigned int runflags) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci return runflags & COMEDI_SRF_ERROR; 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci/** 66962306a36Sopenharmony_ci * comedi_is_subdevice_running() - Check if async command running on subdevice 67062306a36Sopenharmony_ci * @s: COMEDI subdevice. 67162306a36Sopenharmony_ci * 67262306a36Sopenharmony_ci * Return: %true if an asynchronous COMEDI command is active on the 67362306a36Sopenharmony_ci * subdevice, else %false. 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_cibool comedi_is_subdevice_running(struct comedi_subdevice *s) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci unsigned int runflags = comedi_get_subdevice_runflags(s); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return comedi_is_runflags_running(runflags); 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_is_subdevice_running); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic bool __comedi_is_subdevice_running(struct comedi_subdevice *s) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci unsigned int runflags = __comedi_get_subdevice_runflags(s); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci return comedi_is_runflags_running(runflags); 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cibool comedi_can_auto_free_spriv(struct comedi_subdevice *s) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci unsigned int runflags = __comedi_get_subdevice_runflags(s); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci return runflags & COMEDI_SRF_FREE_SPRIV; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci/** 69862306a36Sopenharmony_ci * comedi_set_spriv_auto_free() - Mark subdevice private data as freeable 69962306a36Sopenharmony_ci * @s: COMEDI subdevice. 70062306a36Sopenharmony_ci * 70162306a36Sopenharmony_ci * Mark the subdevice as having a pointer to private data that can be 70262306a36Sopenharmony_ci * automatically freed when the COMEDI device is detached from the low-level 70362306a36Sopenharmony_ci * driver. 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_civoid comedi_set_spriv_auto_free(struct comedi_subdevice *s) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci __comedi_set_subdevice_runflags(s, COMEDI_SRF_FREE_SPRIV); 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_set_spriv_auto_free); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci/** 71262306a36Sopenharmony_ci * comedi_alloc_spriv - Allocate memory for the subdevice private data 71362306a36Sopenharmony_ci * @s: COMEDI subdevice. 71462306a36Sopenharmony_ci * @size: Size of the memory to allocate. 71562306a36Sopenharmony_ci * 71662306a36Sopenharmony_ci * Allocate memory for the subdevice private data and point @s->private 71762306a36Sopenharmony_ci * to it. The memory will be freed automatically when the COMEDI device 71862306a36Sopenharmony_ci * is detached from the low-level driver. 71962306a36Sopenharmony_ci * 72062306a36Sopenharmony_ci * Return: A pointer to the allocated memory @s->private on success. 72162306a36Sopenharmony_ci * Return NULL on failure. 72262306a36Sopenharmony_ci */ 72362306a36Sopenharmony_civoid *comedi_alloc_spriv(struct comedi_subdevice *s, size_t size) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci s->private = kzalloc(size, GFP_KERNEL); 72662306a36Sopenharmony_ci if (s->private) 72762306a36Sopenharmony_ci comedi_set_spriv_auto_free(s); 72862306a36Sopenharmony_ci return s->private; 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_alloc_spriv); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci/* 73362306a36Sopenharmony_ci * This function restores a subdevice to an idle state. 73462306a36Sopenharmony_ci */ 73562306a36Sopenharmony_cistatic void do_become_nonbusy(struct comedi_device *dev, 73662306a36Sopenharmony_ci struct comedi_subdevice *s) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci struct comedi_async *async = s->async; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 74162306a36Sopenharmony_ci comedi_update_subdevice_runflags(s, COMEDI_SRF_RUNNING, 0); 74262306a36Sopenharmony_ci if (async) { 74362306a36Sopenharmony_ci comedi_buf_reset(s); 74462306a36Sopenharmony_ci async->inttrig = NULL; 74562306a36Sopenharmony_ci kfree(async->cmd.chanlist); 74662306a36Sopenharmony_ci async->cmd.chanlist = NULL; 74762306a36Sopenharmony_ci s->busy = NULL; 74862306a36Sopenharmony_ci wake_up_interruptible_all(&async->wait_head); 74962306a36Sopenharmony_ci } else { 75062306a36Sopenharmony_ci dev_err(dev->class_dev, 75162306a36Sopenharmony_ci "BUG: (?) %s called with async=NULL\n", __func__); 75262306a36Sopenharmony_ci s->busy = NULL; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic int do_cancel(struct comedi_device *dev, struct comedi_subdevice *s) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci int ret = 0; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 76162306a36Sopenharmony_ci if (comedi_is_subdevice_running(s) && s->cancel) 76262306a36Sopenharmony_ci ret = s->cancel(dev, s); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci do_become_nonbusy(dev, s); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci return ret; 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_civoid comedi_device_cancel_all(struct comedi_device *dev) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci struct comedi_subdevice *s; 77262306a36Sopenharmony_ci int i; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 77562306a36Sopenharmony_ci if (!dev->attached) 77662306a36Sopenharmony_ci return; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci for (i = 0; i < dev->n_subdevices; i++) { 77962306a36Sopenharmony_ci s = &dev->subdevices[i]; 78062306a36Sopenharmony_ci if (s->async) 78162306a36Sopenharmony_ci do_cancel(dev, s); 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic int is_device_busy(struct comedi_device *dev) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci struct comedi_subdevice *s; 78862306a36Sopenharmony_ci int i; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 79162306a36Sopenharmony_ci if (!dev->attached) 79262306a36Sopenharmony_ci return 0; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci for (i = 0; i < dev->n_subdevices; i++) { 79562306a36Sopenharmony_ci s = &dev->subdevices[i]; 79662306a36Sopenharmony_ci if (s->busy) 79762306a36Sopenharmony_ci return 1; 79862306a36Sopenharmony_ci if (s->async && comedi_buf_is_mmapped(s)) 79962306a36Sopenharmony_ci return 1; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci return 0; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci/* 80662306a36Sopenharmony_ci * COMEDI_DEVCONFIG ioctl 80762306a36Sopenharmony_ci * attaches (and configures) or detaches a legacy device 80862306a36Sopenharmony_ci * 80962306a36Sopenharmony_ci * arg: 81062306a36Sopenharmony_ci * pointer to comedi_devconfig structure (NULL if detaching) 81162306a36Sopenharmony_ci * 81262306a36Sopenharmony_ci * reads: 81362306a36Sopenharmony_ci * comedi_devconfig structure (if attaching) 81462306a36Sopenharmony_ci * 81562306a36Sopenharmony_ci * writes: 81662306a36Sopenharmony_ci * nothing 81762306a36Sopenharmony_ci */ 81862306a36Sopenharmony_cistatic int do_devconfig_ioctl(struct comedi_device *dev, 81962306a36Sopenharmony_ci struct comedi_devconfig __user *arg) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci struct comedi_devconfig it; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 82462306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 82562306a36Sopenharmony_ci return -EPERM; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if (!arg) { 82862306a36Sopenharmony_ci if (is_device_busy(dev)) 82962306a36Sopenharmony_ci return -EBUSY; 83062306a36Sopenharmony_ci if (dev->attached) { 83162306a36Sopenharmony_ci struct module *driver_module = dev->driver->module; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci comedi_device_detach(dev); 83462306a36Sopenharmony_ci module_put(driver_module); 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci return 0; 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (copy_from_user(&it, arg, sizeof(it))) 84062306a36Sopenharmony_ci return -EFAULT; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci it.board_name[COMEDI_NAMELEN - 1] = 0; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci if (it.options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) { 84562306a36Sopenharmony_ci dev_warn(dev->class_dev, 84662306a36Sopenharmony_ci "comedi_config --init_data is deprecated\n"); 84762306a36Sopenharmony_ci return -EINVAL; 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci if (dev->minor >= comedi_num_legacy_minors) 85162306a36Sopenharmony_ci /* don't re-use dynamically allocated comedi devices */ 85262306a36Sopenharmony_ci return -EBUSY; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci /* This increments the driver module count on success. */ 85562306a36Sopenharmony_ci return comedi_device_attach(dev, &it); 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci/* 85962306a36Sopenharmony_ci * COMEDI_BUFCONFIG ioctl 86062306a36Sopenharmony_ci * buffer configuration 86162306a36Sopenharmony_ci * 86262306a36Sopenharmony_ci * arg: 86362306a36Sopenharmony_ci * pointer to comedi_bufconfig structure 86462306a36Sopenharmony_ci * 86562306a36Sopenharmony_ci * reads: 86662306a36Sopenharmony_ci * comedi_bufconfig structure 86762306a36Sopenharmony_ci * 86862306a36Sopenharmony_ci * writes: 86962306a36Sopenharmony_ci * modified comedi_bufconfig structure 87062306a36Sopenharmony_ci */ 87162306a36Sopenharmony_cistatic int do_bufconfig_ioctl(struct comedi_device *dev, 87262306a36Sopenharmony_ci struct comedi_bufconfig __user *arg) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci struct comedi_bufconfig bc; 87562306a36Sopenharmony_ci struct comedi_async *async; 87662306a36Sopenharmony_ci struct comedi_subdevice *s; 87762306a36Sopenharmony_ci int retval = 0; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 88062306a36Sopenharmony_ci if (copy_from_user(&bc, arg, sizeof(bc))) 88162306a36Sopenharmony_ci return -EFAULT; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (bc.subdevice >= dev->n_subdevices) 88462306a36Sopenharmony_ci return -EINVAL; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci s = &dev->subdevices[bc.subdevice]; 88762306a36Sopenharmony_ci async = s->async; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci if (!async) { 89062306a36Sopenharmony_ci dev_dbg(dev->class_dev, 89162306a36Sopenharmony_ci "subdevice does not have async capability\n"); 89262306a36Sopenharmony_ci bc.size = 0; 89362306a36Sopenharmony_ci bc.maximum_size = 0; 89462306a36Sopenharmony_ci goto copyback; 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci if (bc.maximum_size) { 89862306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 89962306a36Sopenharmony_ci return -EPERM; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci async->max_bufsize = bc.maximum_size; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if (bc.size) { 90562306a36Sopenharmony_ci retval = resize_async_buffer(dev, s, bc.size); 90662306a36Sopenharmony_ci if (retval < 0) 90762306a36Sopenharmony_ci return retval; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci bc.size = async->prealloc_bufsz; 91162306a36Sopenharmony_ci bc.maximum_size = async->max_bufsize; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cicopyback: 91462306a36Sopenharmony_ci if (copy_to_user(arg, &bc, sizeof(bc))) 91562306a36Sopenharmony_ci return -EFAULT; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci return 0; 91862306a36Sopenharmony_ci} 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci/* 92162306a36Sopenharmony_ci * COMEDI_DEVINFO ioctl 92262306a36Sopenharmony_ci * device info 92362306a36Sopenharmony_ci * 92462306a36Sopenharmony_ci * arg: 92562306a36Sopenharmony_ci * pointer to comedi_devinfo structure 92662306a36Sopenharmony_ci * 92762306a36Sopenharmony_ci * reads: 92862306a36Sopenharmony_ci * nothing 92962306a36Sopenharmony_ci * 93062306a36Sopenharmony_ci * writes: 93162306a36Sopenharmony_ci * comedi_devinfo structure 93262306a36Sopenharmony_ci */ 93362306a36Sopenharmony_cistatic int do_devinfo_ioctl(struct comedi_device *dev, 93462306a36Sopenharmony_ci struct comedi_devinfo __user *arg, 93562306a36Sopenharmony_ci struct file *file) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci struct comedi_subdevice *s; 93862306a36Sopenharmony_ci struct comedi_devinfo devinfo; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 94162306a36Sopenharmony_ci memset(&devinfo, 0, sizeof(devinfo)); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci /* fill devinfo structure */ 94462306a36Sopenharmony_ci devinfo.version_code = COMEDI_VERSION_CODE; 94562306a36Sopenharmony_ci devinfo.n_subdevs = dev->n_subdevices; 94662306a36Sopenharmony_ci strscpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN); 94762306a36Sopenharmony_ci strscpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci s = comedi_file_read_subdevice(file); 95062306a36Sopenharmony_ci if (s) 95162306a36Sopenharmony_ci devinfo.read_subdevice = s->index; 95262306a36Sopenharmony_ci else 95362306a36Sopenharmony_ci devinfo.read_subdevice = -1; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci s = comedi_file_write_subdevice(file); 95662306a36Sopenharmony_ci if (s) 95762306a36Sopenharmony_ci devinfo.write_subdevice = s->index; 95862306a36Sopenharmony_ci else 95962306a36Sopenharmony_ci devinfo.write_subdevice = -1; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci if (copy_to_user(arg, &devinfo, sizeof(devinfo))) 96262306a36Sopenharmony_ci return -EFAULT; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci return 0; 96562306a36Sopenharmony_ci} 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci/* 96862306a36Sopenharmony_ci * COMEDI_SUBDINFO ioctl 96962306a36Sopenharmony_ci * subdevices info 97062306a36Sopenharmony_ci * 97162306a36Sopenharmony_ci * arg: 97262306a36Sopenharmony_ci * pointer to array of comedi_subdinfo structures 97362306a36Sopenharmony_ci * 97462306a36Sopenharmony_ci * reads: 97562306a36Sopenharmony_ci * nothing 97662306a36Sopenharmony_ci * 97762306a36Sopenharmony_ci * writes: 97862306a36Sopenharmony_ci * array of comedi_subdinfo structures 97962306a36Sopenharmony_ci */ 98062306a36Sopenharmony_cistatic int do_subdinfo_ioctl(struct comedi_device *dev, 98162306a36Sopenharmony_ci struct comedi_subdinfo __user *arg, void *file) 98262306a36Sopenharmony_ci{ 98362306a36Sopenharmony_ci int ret, i; 98462306a36Sopenharmony_ci struct comedi_subdinfo *tmp, *us; 98562306a36Sopenharmony_ci struct comedi_subdevice *s; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 98862306a36Sopenharmony_ci tmp = kcalloc(dev->n_subdevices, sizeof(*tmp), GFP_KERNEL); 98962306a36Sopenharmony_ci if (!tmp) 99062306a36Sopenharmony_ci return -ENOMEM; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci /* fill subdinfo structs */ 99362306a36Sopenharmony_ci for (i = 0; i < dev->n_subdevices; i++) { 99462306a36Sopenharmony_ci s = &dev->subdevices[i]; 99562306a36Sopenharmony_ci us = tmp + i; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci us->type = s->type; 99862306a36Sopenharmony_ci us->n_chan = s->n_chan; 99962306a36Sopenharmony_ci us->subd_flags = s->subdev_flags; 100062306a36Sopenharmony_ci if (comedi_is_subdevice_running(s)) 100162306a36Sopenharmony_ci us->subd_flags |= SDF_RUNNING; 100262306a36Sopenharmony_ci#define TIMER_nanosec 5 /* backwards compatibility */ 100362306a36Sopenharmony_ci us->timer_type = TIMER_nanosec; 100462306a36Sopenharmony_ci us->len_chanlist = s->len_chanlist; 100562306a36Sopenharmony_ci us->maxdata = s->maxdata; 100662306a36Sopenharmony_ci if (s->range_table) { 100762306a36Sopenharmony_ci us->range_type = 100862306a36Sopenharmony_ci (i << 24) | (0 << 16) | (s->range_table->length); 100962306a36Sopenharmony_ci } else { 101062306a36Sopenharmony_ci us->range_type = 0; /* XXX */ 101162306a36Sopenharmony_ci } 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci if (s->busy) 101462306a36Sopenharmony_ci us->subd_flags |= SDF_BUSY; 101562306a36Sopenharmony_ci if (s->busy == file) 101662306a36Sopenharmony_ci us->subd_flags |= SDF_BUSY_OWNER; 101762306a36Sopenharmony_ci if (s->lock) 101862306a36Sopenharmony_ci us->subd_flags |= SDF_LOCKED; 101962306a36Sopenharmony_ci if (s->lock == file) 102062306a36Sopenharmony_ci us->subd_flags |= SDF_LOCK_OWNER; 102162306a36Sopenharmony_ci if (!s->maxdata && s->maxdata_list) 102262306a36Sopenharmony_ci us->subd_flags |= SDF_MAXDATA; 102362306a36Sopenharmony_ci if (s->range_table_list) 102462306a36Sopenharmony_ci us->subd_flags |= SDF_RANGETYPE; 102562306a36Sopenharmony_ci if (s->do_cmd) 102662306a36Sopenharmony_ci us->subd_flags |= SDF_CMD; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci if (s->insn_bits != &insn_inval) 102962306a36Sopenharmony_ci us->insn_bits_support = COMEDI_SUPPORTED; 103062306a36Sopenharmony_ci else 103162306a36Sopenharmony_ci us->insn_bits_support = COMEDI_UNSUPPORTED; 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci ret = copy_to_user(arg, tmp, dev->n_subdevices * sizeof(*tmp)); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci kfree(tmp); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci return ret ? -EFAULT : 0; 103962306a36Sopenharmony_ci} 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci/* 104262306a36Sopenharmony_ci * COMEDI_CHANINFO ioctl 104362306a36Sopenharmony_ci * subdevice channel info 104462306a36Sopenharmony_ci * 104562306a36Sopenharmony_ci * arg: 104662306a36Sopenharmony_ci * pointer to comedi_chaninfo structure 104762306a36Sopenharmony_ci * 104862306a36Sopenharmony_ci * reads: 104962306a36Sopenharmony_ci * comedi_chaninfo structure 105062306a36Sopenharmony_ci * 105162306a36Sopenharmony_ci * writes: 105262306a36Sopenharmony_ci * array of maxdata values to chaninfo->maxdata_list if requested 105362306a36Sopenharmony_ci * array of range table lengths to chaninfo->range_table_list if requested 105462306a36Sopenharmony_ci */ 105562306a36Sopenharmony_cistatic int do_chaninfo_ioctl(struct comedi_device *dev, 105662306a36Sopenharmony_ci struct comedi_chaninfo *it) 105762306a36Sopenharmony_ci{ 105862306a36Sopenharmony_ci struct comedi_subdevice *s; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci if (it->subdev >= dev->n_subdevices) 106362306a36Sopenharmony_ci return -EINVAL; 106462306a36Sopenharmony_ci s = &dev->subdevices[it->subdev]; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci if (it->maxdata_list) { 106762306a36Sopenharmony_ci if (s->maxdata || !s->maxdata_list) 106862306a36Sopenharmony_ci return -EINVAL; 106962306a36Sopenharmony_ci if (copy_to_user(it->maxdata_list, s->maxdata_list, 107062306a36Sopenharmony_ci s->n_chan * sizeof(unsigned int))) 107162306a36Sopenharmony_ci return -EFAULT; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci if (it->flaglist) 107562306a36Sopenharmony_ci return -EINVAL; /* flaglist not supported */ 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci if (it->rangelist) { 107862306a36Sopenharmony_ci int i; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci if (!s->range_table_list) 108162306a36Sopenharmony_ci return -EINVAL; 108262306a36Sopenharmony_ci for (i = 0; i < s->n_chan; i++) { 108362306a36Sopenharmony_ci int x; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci x = (dev->minor << 28) | (it->subdev << 24) | (i << 16) | 108662306a36Sopenharmony_ci (s->range_table_list[i]->length); 108762306a36Sopenharmony_ci if (put_user(x, it->rangelist + i)) 108862306a36Sopenharmony_ci return -EFAULT; 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci } 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci return 0; 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci/* 109662306a36Sopenharmony_ci * COMEDI_BUFINFO ioctl 109762306a36Sopenharmony_ci * buffer information 109862306a36Sopenharmony_ci * 109962306a36Sopenharmony_ci * arg: 110062306a36Sopenharmony_ci * pointer to comedi_bufinfo structure 110162306a36Sopenharmony_ci * 110262306a36Sopenharmony_ci * reads: 110362306a36Sopenharmony_ci * comedi_bufinfo structure 110462306a36Sopenharmony_ci * 110562306a36Sopenharmony_ci * writes: 110662306a36Sopenharmony_ci * modified comedi_bufinfo structure 110762306a36Sopenharmony_ci */ 110862306a36Sopenharmony_cistatic int do_bufinfo_ioctl(struct comedi_device *dev, 110962306a36Sopenharmony_ci struct comedi_bufinfo __user *arg, void *file) 111062306a36Sopenharmony_ci{ 111162306a36Sopenharmony_ci struct comedi_bufinfo bi; 111262306a36Sopenharmony_ci struct comedi_subdevice *s; 111362306a36Sopenharmony_ci struct comedi_async *async; 111462306a36Sopenharmony_ci unsigned int runflags; 111562306a36Sopenharmony_ci int retval = 0; 111662306a36Sopenharmony_ci bool become_nonbusy = false; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 111962306a36Sopenharmony_ci if (copy_from_user(&bi, arg, sizeof(bi))) 112062306a36Sopenharmony_ci return -EFAULT; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci if (bi.subdevice >= dev->n_subdevices) 112362306a36Sopenharmony_ci return -EINVAL; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci s = &dev->subdevices[bi.subdevice]; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci async = s->async; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci if (!async || s->busy != file) 113062306a36Sopenharmony_ci return -EINVAL; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci runflags = comedi_get_subdevice_runflags(s); 113362306a36Sopenharmony_ci if (!(async->cmd.flags & CMDF_WRITE)) { 113462306a36Sopenharmony_ci /* command was set up in "read" direction */ 113562306a36Sopenharmony_ci if (bi.bytes_read) { 113662306a36Sopenharmony_ci comedi_buf_read_alloc(s, bi.bytes_read); 113762306a36Sopenharmony_ci bi.bytes_read = comedi_buf_read_free(s, bi.bytes_read); 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci /* 114062306a36Sopenharmony_ci * If nothing left to read, and command has stopped, and 114162306a36Sopenharmony_ci * {"read" position not updated or command stopped normally}, 114262306a36Sopenharmony_ci * then become non-busy. 114362306a36Sopenharmony_ci */ 114462306a36Sopenharmony_ci if (comedi_buf_read_n_available(s) == 0 && 114562306a36Sopenharmony_ci !comedi_is_runflags_running(runflags) && 114662306a36Sopenharmony_ci (bi.bytes_read == 0 || 114762306a36Sopenharmony_ci !comedi_is_runflags_in_error(runflags))) { 114862306a36Sopenharmony_ci become_nonbusy = true; 114962306a36Sopenharmony_ci if (comedi_is_runflags_in_error(runflags)) 115062306a36Sopenharmony_ci retval = -EPIPE; 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci bi.bytes_written = 0; 115362306a36Sopenharmony_ci } else { 115462306a36Sopenharmony_ci /* command was set up in "write" direction */ 115562306a36Sopenharmony_ci if (!comedi_is_runflags_running(runflags)) { 115662306a36Sopenharmony_ci bi.bytes_written = 0; 115762306a36Sopenharmony_ci become_nonbusy = true; 115862306a36Sopenharmony_ci if (comedi_is_runflags_in_error(runflags)) 115962306a36Sopenharmony_ci retval = -EPIPE; 116062306a36Sopenharmony_ci } else if (bi.bytes_written) { 116162306a36Sopenharmony_ci comedi_buf_write_alloc(s, bi.bytes_written); 116262306a36Sopenharmony_ci bi.bytes_written = 116362306a36Sopenharmony_ci comedi_buf_write_free(s, bi.bytes_written); 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci bi.bytes_read = 0; 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci bi.buf_write_count = async->buf_write_count; 116962306a36Sopenharmony_ci bi.buf_write_ptr = async->buf_write_ptr; 117062306a36Sopenharmony_ci bi.buf_read_count = async->buf_read_count; 117162306a36Sopenharmony_ci bi.buf_read_ptr = async->buf_read_ptr; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci if (become_nonbusy) 117462306a36Sopenharmony_ci do_become_nonbusy(dev, s); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci if (retval) 117762306a36Sopenharmony_ci return retval; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci if (copy_to_user(arg, &bi, sizeof(bi))) 118062306a36Sopenharmony_ci return -EFAULT; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci return 0; 118362306a36Sopenharmony_ci} 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_cistatic int check_insn_config_length(struct comedi_insn *insn, 118662306a36Sopenharmony_ci unsigned int *data) 118762306a36Sopenharmony_ci{ 118862306a36Sopenharmony_ci if (insn->n < 1) 118962306a36Sopenharmony_ci return -EINVAL; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci switch (data[0]) { 119262306a36Sopenharmony_ci case INSN_CONFIG_DIO_OUTPUT: 119362306a36Sopenharmony_ci case INSN_CONFIG_DIO_INPUT: 119462306a36Sopenharmony_ci case INSN_CONFIG_DISARM: 119562306a36Sopenharmony_ci case INSN_CONFIG_RESET: 119662306a36Sopenharmony_ci if (insn->n == 1) 119762306a36Sopenharmony_ci return 0; 119862306a36Sopenharmony_ci break; 119962306a36Sopenharmony_ci case INSN_CONFIG_ARM: 120062306a36Sopenharmony_ci case INSN_CONFIG_DIO_QUERY: 120162306a36Sopenharmony_ci case INSN_CONFIG_BLOCK_SIZE: 120262306a36Sopenharmony_ci case INSN_CONFIG_FILTER: 120362306a36Sopenharmony_ci case INSN_CONFIG_SERIAL_CLOCK: 120462306a36Sopenharmony_ci case INSN_CONFIG_BIDIRECTIONAL_DATA: 120562306a36Sopenharmony_ci case INSN_CONFIG_ALT_SOURCE: 120662306a36Sopenharmony_ci case INSN_CONFIG_SET_COUNTER_MODE: 120762306a36Sopenharmony_ci case INSN_CONFIG_8254_READ_STATUS: 120862306a36Sopenharmony_ci case INSN_CONFIG_SET_ROUTING: 120962306a36Sopenharmony_ci case INSN_CONFIG_GET_ROUTING: 121062306a36Sopenharmony_ci case INSN_CONFIG_GET_PWM_STATUS: 121162306a36Sopenharmony_ci case INSN_CONFIG_PWM_SET_PERIOD: 121262306a36Sopenharmony_ci case INSN_CONFIG_PWM_GET_PERIOD: 121362306a36Sopenharmony_ci if (insn->n == 2) 121462306a36Sopenharmony_ci return 0; 121562306a36Sopenharmony_ci break; 121662306a36Sopenharmony_ci case INSN_CONFIG_SET_GATE_SRC: 121762306a36Sopenharmony_ci case INSN_CONFIG_GET_GATE_SRC: 121862306a36Sopenharmony_ci case INSN_CONFIG_SET_CLOCK_SRC: 121962306a36Sopenharmony_ci case INSN_CONFIG_GET_CLOCK_SRC: 122062306a36Sopenharmony_ci case INSN_CONFIG_SET_OTHER_SRC: 122162306a36Sopenharmony_ci case INSN_CONFIG_GET_COUNTER_STATUS: 122262306a36Sopenharmony_ci case INSN_CONFIG_GET_PWM_OUTPUT: 122362306a36Sopenharmony_ci case INSN_CONFIG_PWM_SET_H_BRIDGE: 122462306a36Sopenharmony_ci case INSN_CONFIG_PWM_GET_H_BRIDGE: 122562306a36Sopenharmony_ci case INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE: 122662306a36Sopenharmony_ci if (insn->n == 3) 122762306a36Sopenharmony_ci return 0; 122862306a36Sopenharmony_ci break; 122962306a36Sopenharmony_ci case INSN_CONFIG_PWM_OUTPUT: 123062306a36Sopenharmony_ci case INSN_CONFIG_ANALOG_TRIG: 123162306a36Sopenharmony_ci case INSN_CONFIG_TIMER_1: 123262306a36Sopenharmony_ci if (insn->n == 5) 123362306a36Sopenharmony_ci return 0; 123462306a36Sopenharmony_ci break; 123562306a36Sopenharmony_ci case INSN_CONFIG_DIGITAL_TRIG: 123662306a36Sopenharmony_ci if (insn->n == 6) 123762306a36Sopenharmony_ci return 0; 123862306a36Sopenharmony_ci break; 123962306a36Sopenharmony_ci case INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS: 124062306a36Sopenharmony_ci if (insn->n >= 4) 124162306a36Sopenharmony_ci return 0; 124262306a36Sopenharmony_ci break; 124362306a36Sopenharmony_ci /* 124462306a36Sopenharmony_ci * by default we allow the insn since we don't have checks for 124562306a36Sopenharmony_ci * all possible cases yet 124662306a36Sopenharmony_ci */ 124762306a36Sopenharmony_ci default: 124862306a36Sopenharmony_ci pr_warn("No check for data length of config insn id %i is implemented\n", 124962306a36Sopenharmony_ci data[0]); 125062306a36Sopenharmony_ci pr_warn("Add a check to %s in %s\n", __func__, __FILE__); 125162306a36Sopenharmony_ci pr_warn("Assuming n=%i is correct\n", insn->n); 125262306a36Sopenharmony_ci return 0; 125362306a36Sopenharmony_ci } 125462306a36Sopenharmony_ci return -EINVAL; 125562306a36Sopenharmony_ci} 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_cistatic int check_insn_device_config_length(struct comedi_insn *insn, 125862306a36Sopenharmony_ci unsigned int *data) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci if (insn->n < 1) 126162306a36Sopenharmony_ci return -EINVAL; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci switch (data[0]) { 126462306a36Sopenharmony_ci case INSN_DEVICE_CONFIG_TEST_ROUTE: 126562306a36Sopenharmony_ci case INSN_DEVICE_CONFIG_CONNECT_ROUTE: 126662306a36Sopenharmony_ci case INSN_DEVICE_CONFIG_DISCONNECT_ROUTE: 126762306a36Sopenharmony_ci if (insn->n == 3) 126862306a36Sopenharmony_ci return 0; 126962306a36Sopenharmony_ci break; 127062306a36Sopenharmony_ci case INSN_DEVICE_CONFIG_GET_ROUTES: 127162306a36Sopenharmony_ci /* 127262306a36Sopenharmony_ci * Big enough for config_id and the length of the userland 127362306a36Sopenharmony_ci * memory buffer. Additional length should be in factors of 2 127462306a36Sopenharmony_ci * to communicate any returned route pairs (source,destination). 127562306a36Sopenharmony_ci */ 127662306a36Sopenharmony_ci if (insn->n >= 2) 127762306a36Sopenharmony_ci return 0; 127862306a36Sopenharmony_ci break; 127962306a36Sopenharmony_ci } 128062306a36Sopenharmony_ci return -EINVAL; 128162306a36Sopenharmony_ci} 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci/** 128462306a36Sopenharmony_ci * get_valid_routes() - Calls low-level driver get_valid_routes function to 128562306a36Sopenharmony_ci * either return a count of valid routes to user, or copy 128662306a36Sopenharmony_ci * of list of all valid device routes to buffer in 128762306a36Sopenharmony_ci * userspace. 128862306a36Sopenharmony_ci * @dev: comedi device pointer 128962306a36Sopenharmony_ci * @data: data from user insn call. The length of the data must be >= 2. 129062306a36Sopenharmony_ci * data[0] must contain the INSN_DEVICE_CONFIG config_id. 129162306a36Sopenharmony_ci * data[1](input) contains the number of _pairs_ for which memory is 129262306a36Sopenharmony_ci * allotted from the user. If the user specifies '0', then only 129362306a36Sopenharmony_ci * the number of pairs available is returned. 129462306a36Sopenharmony_ci * data[1](output) returns either the number of pairs available (if none 129562306a36Sopenharmony_ci * where requested) or the number of _pairs_ that are copied back 129662306a36Sopenharmony_ci * to the user. 129762306a36Sopenharmony_ci * data[2::2] returns each (source, destination) pair. 129862306a36Sopenharmony_ci * 129962306a36Sopenharmony_ci * Return: -EINVAL if low-level driver does not allocate and return routes as 130062306a36Sopenharmony_ci * expected. Returns 0 otherwise. 130162306a36Sopenharmony_ci */ 130262306a36Sopenharmony_cistatic int get_valid_routes(struct comedi_device *dev, unsigned int *data) 130362306a36Sopenharmony_ci{ 130462306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 130562306a36Sopenharmony_ci data[1] = dev->get_valid_routes(dev, data[1], data + 2); 130662306a36Sopenharmony_ci return 0; 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_cistatic int parse_insn(struct comedi_device *dev, struct comedi_insn *insn, 131062306a36Sopenharmony_ci unsigned int *data, void *file) 131162306a36Sopenharmony_ci{ 131262306a36Sopenharmony_ci struct comedi_subdevice *s; 131362306a36Sopenharmony_ci int ret = 0; 131462306a36Sopenharmony_ci int i; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 131762306a36Sopenharmony_ci if (insn->insn & INSN_MASK_SPECIAL) { 131862306a36Sopenharmony_ci /* a non-subdevice instruction */ 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci switch (insn->insn) { 132162306a36Sopenharmony_ci case INSN_GTOD: 132262306a36Sopenharmony_ci { 132362306a36Sopenharmony_ci struct timespec64 tv; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci if (insn->n != 2) { 132662306a36Sopenharmony_ci ret = -EINVAL; 132762306a36Sopenharmony_ci break; 132862306a36Sopenharmony_ci } 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci ktime_get_real_ts64(&tv); 133162306a36Sopenharmony_ci /* unsigned data safe until 2106 */ 133262306a36Sopenharmony_ci data[0] = (unsigned int)tv.tv_sec; 133362306a36Sopenharmony_ci data[1] = tv.tv_nsec / NSEC_PER_USEC; 133462306a36Sopenharmony_ci ret = 2; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci break; 133762306a36Sopenharmony_ci } 133862306a36Sopenharmony_ci case INSN_WAIT: 133962306a36Sopenharmony_ci if (insn->n != 1 || data[0] >= 100000) { 134062306a36Sopenharmony_ci ret = -EINVAL; 134162306a36Sopenharmony_ci break; 134262306a36Sopenharmony_ci } 134362306a36Sopenharmony_ci udelay(data[0] / 1000); 134462306a36Sopenharmony_ci ret = 1; 134562306a36Sopenharmony_ci break; 134662306a36Sopenharmony_ci case INSN_INTTRIG: 134762306a36Sopenharmony_ci if (insn->n != 1) { 134862306a36Sopenharmony_ci ret = -EINVAL; 134962306a36Sopenharmony_ci break; 135062306a36Sopenharmony_ci } 135162306a36Sopenharmony_ci if (insn->subdev >= dev->n_subdevices) { 135262306a36Sopenharmony_ci dev_dbg(dev->class_dev, 135362306a36Sopenharmony_ci "%d not usable subdevice\n", 135462306a36Sopenharmony_ci insn->subdev); 135562306a36Sopenharmony_ci ret = -EINVAL; 135662306a36Sopenharmony_ci break; 135762306a36Sopenharmony_ci } 135862306a36Sopenharmony_ci s = &dev->subdevices[insn->subdev]; 135962306a36Sopenharmony_ci if (!s->async) { 136062306a36Sopenharmony_ci dev_dbg(dev->class_dev, "no async\n"); 136162306a36Sopenharmony_ci ret = -EINVAL; 136262306a36Sopenharmony_ci break; 136362306a36Sopenharmony_ci } 136462306a36Sopenharmony_ci if (!s->async->inttrig) { 136562306a36Sopenharmony_ci dev_dbg(dev->class_dev, "no inttrig\n"); 136662306a36Sopenharmony_ci ret = -EAGAIN; 136762306a36Sopenharmony_ci break; 136862306a36Sopenharmony_ci } 136962306a36Sopenharmony_ci ret = s->async->inttrig(dev, s, data[0]); 137062306a36Sopenharmony_ci if (ret >= 0) 137162306a36Sopenharmony_ci ret = 1; 137262306a36Sopenharmony_ci break; 137362306a36Sopenharmony_ci case INSN_DEVICE_CONFIG: 137462306a36Sopenharmony_ci ret = check_insn_device_config_length(insn, data); 137562306a36Sopenharmony_ci if (ret) 137662306a36Sopenharmony_ci break; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci if (data[0] == INSN_DEVICE_CONFIG_GET_ROUTES) { 137962306a36Sopenharmony_ci /* 138062306a36Sopenharmony_ci * data[1] should be the number of _pairs_ that 138162306a36Sopenharmony_ci * the memory can hold. 138262306a36Sopenharmony_ci */ 138362306a36Sopenharmony_ci data[1] = (insn->n - 2) / 2; 138462306a36Sopenharmony_ci ret = get_valid_routes(dev, data); 138562306a36Sopenharmony_ci break; 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci /* other global device config instructions. */ 138962306a36Sopenharmony_ci ret = dev->insn_device_config(dev, insn, data); 139062306a36Sopenharmony_ci break; 139162306a36Sopenharmony_ci default: 139262306a36Sopenharmony_ci dev_dbg(dev->class_dev, "invalid insn\n"); 139362306a36Sopenharmony_ci ret = -EINVAL; 139462306a36Sopenharmony_ci break; 139562306a36Sopenharmony_ci } 139662306a36Sopenharmony_ci } else { 139762306a36Sopenharmony_ci /* a subdevice instruction */ 139862306a36Sopenharmony_ci unsigned int maxdata; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci if (insn->subdev >= dev->n_subdevices) { 140162306a36Sopenharmony_ci dev_dbg(dev->class_dev, "subdevice %d out of range\n", 140262306a36Sopenharmony_ci insn->subdev); 140362306a36Sopenharmony_ci ret = -EINVAL; 140462306a36Sopenharmony_ci goto out; 140562306a36Sopenharmony_ci } 140662306a36Sopenharmony_ci s = &dev->subdevices[insn->subdev]; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci if (s->type == COMEDI_SUBD_UNUSED) { 140962306a36Sopenharmony_ci dev_dbg(dev->class_dev, "%d not usable subdevice\n", 141062306a36Sopenharmony_ci insn->subdev); 141162306a36Sopenharmony_ci ret = -EIO; 141262306a36Sopenharmony_ci goto out; 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci /* are we locked? (ioctl lock) */ 141662306a36Sopenharmony_ci if (s->lock && s->lock != file) { 141762306a36Sopenharmony_ci dev_dbg(dev->class_dev, "device locked\n"); 141862306a36Sopenharmony_ci ret = -EACCES; 141962306a36Sopenharmony_ci goto out; 142062306a36Sopenharmony_ci } 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci ret = comedi_check_chanlist(s, 1, &insn->chanspec); 142362306a36Sopenharmony_ci if (ret < 0) { 142462306a36Sopenharmony_ci ret = -EINVAL; 142562306a36Sopenharmony_ci dev_dbg(dev->class_dev, "bad chanspec\n"); 142662306a36Sopenharmony_ci goto out; 142762306a36Sopenharmony_ci } 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci if (s->busy) { 143062306a36Sopenharmony_ci ret = -EBUSY; 143162306a36Sopenharmony_ci goto out; 143262306a36Sopenharmony_ci } 143362306a36Sopenharmony_ci /* This looks arbitrary. It is. */ 143462306a36Sopenharmony_ci s->busy = parse_insn; 143562306a36Sopenharmony_ci switch (insn->insn) { 143662306a36Sopenharmony_ci case INSN_READ: 143762306a36Sopenharmony_ci ret = s->insn_read(dev, s, insn, data); 143862306a36Sopenharmony_ci if (ret == -ETIMEDOUT) { 143962306a36Sopenharmony_ci dev_dbg(dev->class_dev, 144062306a36Sopenharmony_ci "subdevice %d read instruction timed out\n", 144162306a36Sopenharmony_ci s->index); 144262306a36Sopenharmony_ci } 144362306a36Sopenharmony_ci break; 144462306a36Sopenharmony_ci case INSN_WRITE: 144562306a36Sopenharmony_ci maxdata = s->maxdata_list 144662306a36Sopenharmony_ci ? s->maxdata_list[CR_CHAN(insn->chanspec)] 144762306a36Sopenharmony_ci : s->maxdata; 144862306a36Sopenharmony_ci for (i = 0; i < insn->n; ++i) { 144962306a36Sopenharmony_ci if (data[i] > maxdata) { 145062306a36Sopenharmony_ci ret = -EINVAL; 145162306a36Sopenharmony_ci dev_dbg(dev->class_dev, 145262306a36Sopenharmony_ci "bad data value(s)\n"); 145362306a36Sopenharmony_ci break; 145462306a36Sopenharmony_ci } 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci if (ret == 0) { 145762306a36Sopenharmony_ci ret = s->insn_write(dev, s, insn, data); 145862306a36Sopenharmony_ci if (ret == -ETIMEDOUT) { 145962306a36Sopenharmony_ci dev_dbg(dev->class_dev, 146062306a36Sopenharmony_ci "subdevice %d write instruction timed out\n", 146162306a36Sopenharmony_ci s->index); 146262306a36Sopenharmony_ci } 146362306a36Sopenharmony_ci } 146462306a36Sopenharmony_ci break; 146562306a36Sopenharmony_ci case INSN_BITS: 146662306a36Sopenharmony_ci if (insn->n != 2) { 146762306a36Sopenharmony_ci ret = -EINVAL; 146862306a36Sopenharmony_ci } else { 146962306a36Sopenharmony_ci /* 147062306a36Sopenharmony_ci * Most drivers ignore the base channel in 147162306a36Sopenharmony_ci * insn->chanspec. Fix this here if 147262306a36Sopenharmony_ci * the subdevice has <= 32 channels. 147362306a36Sopenharmony_ci */ 147462306a36Sopenharmony_ci unsigned int orig_mask = data[0]; 147562306a36Sopenharmony_ci unsigned int shift = 0; 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci if (s->n_chan <= 32) { 147862306a36Sopenharmony_ci shift = CR_CHAN(insn->chanspec); 147962306a36Sopenharmony_ci if (shift > 0) { 148062306a36Sopenharmony_ci insn->chanspec = 0; 148162306a36Sopenharmony_ci data[0] <<= shift; 148262306a36Sopenharmony_ci data[1] <<= shift; 148362306a36Sopenharmony_ci } 148462306a36Sopenharmony_ci } 148562306a36Sopenharmony_ci ret = s->insn_bits(dev, s, insn, data); 148662306a36Sopenharmony_ci data[0] = orig_mask; 148762306a36Sopenharmony_ci if (shift > 0) 148862306a36Sopenharmony_ci data[1] >>= shift; 148962306a36Sopenharmony_ci } 149062306a36Sopenharmony_ci break; 149162306a36Sopenharmony_ci case INSN_CONFIG: 149262306a36Sopenharmony_ci ret = check_insn_config_length(insn, data); 149362306a36Sopenharmony_ci if (ret) 149462306a36Sopenharmony_ci break; 149562306a36Sopenharmony_ci ret = s->insn_config(dev, s, insn, data); 149662306a36Sopenharmony_ci break; 149762306a36Sopenharmony_ci default: 149862306a36Sopenharmony_ci ret = -EINVAL; 149962306a36Sopenharmony_ci break; 150062306a36Sopenharmony_ci } 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci s->busy = NULL; 150362306a36Sopenharmony_ci } 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ciout: 150662306a36Sopenharmony_ci return ret; 150762306a36Sopenharmony_ci} 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci/* 151062306a36Sopenharmony_ci * COMEDI_INSNLIST ioctl 151162306a36Sopenharmony_ci * synchronous instruction list 151262306a36Sopenharmony_ci * 151362306a36Sopenharmony_ci * arg: 151462306a36Sopenharmony_ci * pointer to comedi_insnlist structure 151562306a36Sopenharmony_ci * 151662306a36Sopenharmony_ci * reads: 151762306a36Sopenharmony_ci * comedi_insnlist structure 151862306a36Sopenharmony_ci * array of comedi_insn structures from insnlist->insns pointer 151962306a36Sopenharmony_ci * data (for writes) from insns[].data pointers 152062306a36Sopenharmony_ci * 152162306a36Sopenharmony_ci * writes: 152262306a36Sopenharmony_ci * data (for reads) to insns[].data pointers 152362306a36Sopenharmony_ci */ 152462306a36Sopenharmony_ci/* arbitrary limits */ 152562306a36Sopenharmony_ci#define MIN_SAMPLES 16 152662306a36Sopenharmony_ci#define MAX_SAMPLES 65536 152762306a36Sopenharmony_cistatic int do_insnlist_ioctl(struct comedi_device *dev, 152862306a36Sopenharmony_ci struct comedi_insn *insns, 152962306a36Sopenharmony_ci unsigned int n_insns, 153062306a36Sopenharmony_ci void *file) 153162306a36Sopenharmony_ci{ 153262306a36Sopenharmony_ci unsigned int *data = NULL; 153362306a36Sopenharmony_ci unsigned int max_n_data_required = MIN_SAMPLES; 153462306a36Sopenharmony_ci int i = 0; 153562306a36Sopenharmony_ci int ret = 0; 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci /* Determine maximum memory needed for all instructions. */ 154062306a36Sopenharmony_ci for (i = 0; i < n_insns; ++i) { 154162306a36Sopenharmony_ci if (insns[i].n > MAX_SAMPLES) { 154262306a36Sopenharmony_ci dev_dbg(dev->class_dev, 154362306a36Sopenharmony_ci "number of samples too large\n"); 154462306a36Sopenharmony_ci ret = -EINVAL; 154562306a36Sopenharmony_ci goto error; 154662306a36Sopenharmony_ci } 154762306a36Sopenharmony_ci max_n_data_required = max(max_n_data_required, insns[i].n); 154862306a36Sopenharmony_ci } 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci /* Allocate scratch space for all instruction data. */ 155162306a36Sopenharmony_ci data = kmalloc_array(max_n_data_required, sizeof(unsigned int), 155262306a36Sopenharmony_ci GFP_KERNEL); 155362306a36Sopenharmony_ci if (!data) { 155462306a36Sopenharmony_ci ret = -ENOMEM; 155562306a36Sopenharmony_ci goto error; 155662306a36Sopenharmony_ci } 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci for (i = 0; i < n_insns; ++i) { 155962306a36Sopenharmony_ci if (insns[i].insn & INSN_MASK_WRITE) { 156062306a36Sopenharmony_ci if (copy_from_user(data, insns[i].data, 156162306a36Sopenharmony_ci insns[i].n * sizeof(unsigned int))) { 156262306a36Sopenharmony_ci dev_dbg(dev->class_dev, 156362306a36Sopenharmony_ci "copy_from_user failed\n"); 156462306a36Sopenharmony_ci ret = -EFAULT; 156562306a36Sopenharmony_ci goto error; 156662306a36Sopenharmony_ci } 156762306a36Sopenharmony_ci } 156862306a36Sopenharmony_ci ret = parse_insn(dev, insns + i, data, file); 156962306a36Sopenharmony_ci if (ret < 0) 157062306a36Sopenharmony_ci goto error; 157162306a36Sopenharmony_ci if (insns[i].insn & INSN_MASK_READ) { 157262306a36Sopenharmony_ci if (copy_to_user(insns[i].data, data, 157362306a36Sopenharmony_ci insns[i].n * sizeof(unsigned int))) { 157462306a36Sopenharmony_ci dev_dbg(dev->class_dev, 157562306a36Sopenharmony_ci "copy_to_user failed\n"); 157662306a36Sopenharmony_ci ret = -EFAULT; 157762306a36Sopenharmony_ci goto error; 157862306a36Sopenharmony_ci } 157962306a36Sopenharmony_ci } 158062306a36Sopenharmony_ci if (need_resched()) 158162306a36Sopenharmony_ci schedule(); 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_cierror: 158562306a36Sopenharmony_ci kfree(data); 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci if (ret < 0) 158862306a36Sopenharmony_ci return ret; 158962306a36Sopenharmony_ci return i; 159062306a36Sopenharmony_ci} 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci/* 159362306a36Sopenharmony_ci * COMEDI_INSN ioctl 159462306a36Sopenharmony_ci * synchronous instruction 159562306a36Sopenharmony_ci * 159662306a36Sopenharmony_ci * arg: 159762306a36Sopenharmony_ci * pointer to comedi_insn structure 159862306a36Sopenharmony_ci * 159962306a36Sopenharmony_ci * reads: 160062306a36Sopenharmony_ci * comedi_insn structure 160162306a36Sopenharmony_ci * data (for writes) from insn->data pointer 160262306a36Sopenharmony_ci * 160362306a36Sopenharmony_ci * writes: 160462306a36Sopenharmony_ci * data (for reads) to insn->data pointer 160562306a36Sopenharmony_ci */ 160662306a36Sopenharmony_cistatic int do_insn_ioctl(struct comedi_device *dev, 160762306a36Sopenharmony_ci struct comedi_insn *insn, void *file) 160862306a36Sopenharmony_ci{ 160962306a36Sopenharmony_ci unsigned int *data = NULL; 161062306a36Sopenharmony_ci unsigned int n_data = MIN_SAMPLES; 161162306a36Sopenharmony_ci int ret = 0; 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci n_data = max(n_data, insn->n); 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci /* This is where the behavior of insn and insnlist deviate. */ 161862306a36Sopenharmony_ci if (insn->n > MAX_SAMPLES) { 161962306a36Sopenharmony_ci insn->n = MAX_SAMPLES; 162062306a36Sopenharmony_ci n_data = MAX_SAMPLES; 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci data = kmalloc_array(n_data, sizeof(unsigned int), GFP_KERNEL); 162462306a36Sopenharmony_ci if (!data) { 162562306a36Sopenharmony_ci ret = -ENOMEM; 162662306a36Sopenharmony_ci goto error; 162762306a36Sopenharmony_ci } 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci if (insn->insn & INSN_MASK_WRITE) { 163062306a36Sopenharmony_ci if (copy_from_user(data, 163162306a36Sopenharmony_ci insn->data, 163262306a36Sopenharmony_ci insn->n * sizeof(unsigned int))) { 163362306a36Sopenharmony_ci ret = -EFAULT; 163462306a36Sopenharmony_ci goto error; 163562306a36Sopenharmony_ci } 163662306a36Sopenharmony_ci } 163762306a36Sopenharmony_ci ret = parse_insn(dev, insn, data, file); 163862306a36Sopenharmony_ci if (ret < 0) 163962306a36Sopenharmony_ci goto error; 164062306a36Sopenharmony_ci if (insn->insn & INSN_MASK_READ) { 164162306a36Sopenharmony_ci if (copy_to_user(insn->data, 164262306a36Sopenharmony_ci data, 164362306a36Sopenharmony_ci insn->n * sizeof(unsigned int))) { 164462306a36Sopenharmony_ci ret = -EFAULT; 164562306a36Sopenharmony_ci goto error; 164662306a36Sopenharmony_ci } 164762306a36Sopenharmony_ci } 164862306a36Sopenharmony_ci ret = insn->n; 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_cierror: 165162306a36Sopenharmony_ci kfree(data); 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci return ret; 165462306a36Sopenharmony_ci} 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_cistatic int __comedi_get_user_cmd(struct comedi_device *dev, 165762306a36Sopenharmony_ci struct comedi_cmd *cmd) 165862306a36Sopenharmony_ci{ 165962306a36Sopenharmony_ci struct comedi_subdevice *s; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 166262306a36Sopenharmony_ci if (cmd->subdev >= dev->n_subdevices) { 166362306a36Sopenharmony_ci dev_dbg(dev->class_dev, "%d no such subdevice\n", cmd->subdev); 166462306a36Sopenharmony_ci return -ENODEV; 166562306a36Sopenharmony_ci } 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci s = &dev->subdevices[cmd->subdev]; 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci if (s->type == COMEDI_SUBD_UNUSED) { 167062306a36Sopenharmony_ci dev_dbg(dev->class_dev, "%d not valid subdevice\n", 167162306a36Sopenharmony_ci cmd->subdev); 167262306a36Sopenharmony_ci return -EIO; 167362306a36Sopenharmony_ci } 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci if (!s->do_cmd || !s->do_cmdtest || !s->async) { 167662306a36Sopenharmony_ci dev_dbg(dev->class_dev, 167762306a36Sopenharmony_ci "subdevice %d does not support commands\n", 167862306a36Sopenharmony_ci cmd->subdev); 167962306a36Sopenharmony_ci return -EIO; 168062306a36Sopenharmony_ci } 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci /* make sure channel/gain list isn't too long */ 168362306a36Sopenharmony_ci if (cmd->chanlist_len > s->len_chanlist) { 168462306a36Sopenharmony_ci dev_dbg(dev->class_dev, "channel/gain list too long %d > %d\n", 168562306a36Sopenharmony_ci cmd->chanlist_len, s->len_chanlist); 168662306a36Sopenharmony_ci return -EINVAL; 168762306a36Sopenharmony_ci } 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci /* 169062306a36Sopenharmony_ci * Set the CMDF_WRITE flag to the correct state if the subdevice 169162306a36Sopenharmony_ci * supports only "read" commands or only "write" commands. 169262306a36Sopenharmony_ci */ 169362306a36Sopenharmony_ci switch (s->subdev_flags & (SDF_CMD_READ | SDF_CMD_WRITE)) { 169462306a36Sopenharmony_ci case SDF_CMD_READ: 169562306a36Sopenharmony_ci cmd->flags &= ~CMDF_WRITE; 169662306a36Sopenharmony_ci break; 169762306a36Sopenharmony_ci case SDF_CMD_WRITE: 169862306a36Sopenharmony_ci cmd->flags |= CMDF_WRITE; 169962306a36Sopenharmony_ci break; 170062306a36Sopenharmony_ci default: 170162306a36Sopenharmony_ci break; 170262306a36Sopenharmony_ci } 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci return 0; 170562306a36Sopenharmony_ci} 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_cistatic int __comedi_get_user_chanlist(struct comedi_device *dev, 170862306a36Sopenharmony_ci struct comedi_subdevice *s, 170962306a36Sopenharmony_ci unsigned int __user *user_chanlist, 171062306a36Sopenharmony_ci struct comedi_cmd *cmd) 171162306a36Sopenharmony_ci{ 171262306a36Sopenharmony_ci unsigned int *chanlist; 171362306a36Sopenharmony_ci int ret; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 171662306a36Sopenharmony_ci cmd->chanlist = NULL; 171762306a36Sopenharmony_ci chanlist = memdup_user(user_chanlist, 171862306a36Sopenharmony_ci cmd->chanlist_len * sizeof(unsigned int)); 171962306a36Sopenharmony_ci if (IS_ERR(chanlist)) 172062306a36Sopenharmony_ci return PTR_ERR(chanlist); 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci /* make sure each element in channel/gain list is valid */ 172362306a36Sopenharmony_ci ret = comedi_check_chanlist(s, cmd->chanlist_len, chanlist); 172462306a36Sopenharmony_ci if (ret < 0) { 172562306a36Sopenharmony_ci kfree(chanlist); 172662306a36Sopenharmony_ci return ret; 172762306a36Sopenharmony_ci } 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci cmd->chanlist = chanlist; 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci return 0; 173262306a36Sopenharmony_ci} 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci/* 173562306a36Sopenharmony_ci * COMEDI_CMD ioctl 173662306a36Sopenharmony_ci * asynchronous acquisition command set-up 173762306a36Sopenharmony_ci * 173862306a36Sopenharmony_ci * arg: 173962306a36Sopenharmony_ci * pointer to comedi_cmd structure 174062306a36Sopenharmony_ci * 174162306a36Sopenharmony_ci * reads: 174262306a36Sopenharmony_ci * comedi_cmd structure 174362306a36Sopenharmony_ci * channel/range list from cmd->chanlist pointer 174462306a36Sopenharmony_ci * 174562306a36Sopenharmony_ci * writes: 174662306a36Sopenharmony_ci * possibly modified comedi_cmd structure (when -EAGAIN returned) 174762306a36Sopenharmony_ci */ 174862306a36Sopenharmony_cistatic int do_cmd_ioctl(struct comedi_device *dev, 174962306a36Sopenharmony_ci struct comedi_cmd *cmd, bool *copy, void *file) 175062306a36Sopenharmony_ci{ 175162306a36Sopenharmony_ci struct comedi_subdevice *s; 175262306a36Sopenharmony_ci struct comedi_async *async; 175362306a36Sopenharmony_ci unsigned int __user *user_chanlist; 175462306a36Sopenharmony_ci int ret; 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci /* do some simple cmd validation */ 175962306a36Sopenharmony_ci ret = __comedi_get_user_cmd(dev, cmd); 176062306a36Sopenharmony_ci if (ret) 176162306a36Sopenharmony_ci return ret; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci /* save user's chanlist pointer so it can be restored later */ 176462306a36Sopenharmony_ci user_chanlist = (unsigned int __user *)cmd->chanlist; 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci s = &dev->subdevices[cmd->subdev]; 176762306a36Sopenharmony_ci async = s->async; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci /* are we locked? (ioctl lock) */ 177062306a36Sopenharmony_ci if (s->lock && s->lock != file) { 177162306a36Sopenharmony_ci dev_dbg(dev->class_dev, "subdevice locked\n"); 177262306a36Sopenharmony_ci return -EACCES; 177362306a36Sopenharmony_ci } 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci /* are we busy? */ 177662306a36Sopenharmony_ci if (s->busy) { 177762306a36Sopenharmony_ci dev_dbg(dev->class_dev, "subdevice busy\n"); 177862306a36Sopenharmony_ci return -EBUSY; 177962306a36Sopenharmony_ci } 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci /* make sure channel/gain list isn't too short */ 178262306a36Sopenharmony_ci if (cmd->chanlist_len < 1) { 178362306a36Sopenharmony_ci dev_dbg(dev->class_dev, "channel/gain list too short %u < 1\n", 178462306a36Sopenharmony_ci cmd->chanlist_len); 178562306a36Sopenharmony_ci return -EINVAL; 178662306a36Sopenharmony_ci } 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci async->cmd = *cmd; 178962306a36Sopenharmony_ci async->cmd.data = NULL; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci /* load channel/gain list */ 179262306a36Sopenharmony_ci ret = __comedi_get_user_chanlist(dev, s, user_chanlist, &async->cmd); 179362306a36Sopenharmony_ci if (ret) 179462306a36Sopenharmony_ci goto cleanup; 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci ret = s->do_cmdtest(dev, s, &async->cmd); 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci if (async->cmd.flags & CMDF_BOGUS || ret) { 179962306a36Sopenharmony_ci dev_dbg(dev->class_dev, "test returned %d\n", ret); 180062306a36Sopenharmony_ci *cmd = async->cmd; 180162306a36Sopenharmony_ci /* restore chanlist pointer before copying back */ 180262306a36Sopenharmony_ci cmd->chanlist = (unsigned int __force *)user_chanlist; 180362306a36Sopenharmony_ci cmd->data = NULL; 180462306a36Sopenharmony_ci *copy = true; 180562306a36Sopenharmony_ci ret = -EAGAIN; 180662306a36Sopenharmony_ci goto cleanup; 180762306a36Sopenharmony_ci } 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci if (!async->prealloc_bufsz) { 181062306a36Sopenharmony_ci ret = -ENOMEM; 181162306a36Sopenharmony_ci dev_dbg(dev->class_dev, "no buffer (?)\n"); 181262306a36Sopenharmony_ci goto cleanup; 181362306a36Sopenharmony_ci } 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci comedi_buf_reset(s); 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci async->cb_mask = COMEDI_CB_BLOCK | COMEDI_CB_CANCEL_MASK; 181862306a36Sopenharmony_ci if (async->cmd.flags & CMDF_WAKE_EOS) 181962306a36Sopenharmony_ci async->cb_mask |= COMEDI_CB_EOS; 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci comedi_update_subdevice_runflags(s, COMEDI_SRF_BUSY_MASK, 182262306a36Sopenharmony_ci COMEDI_SRF_RUNNING); 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci /* 182562306a36Sopenharmony_ci * Set s->busy _after_ setting COMEDI_SRF_RUNNING flag to avoid 182662306a36Sopenharmony_ci * race with comedi_read() or comedi_write(). 182762306a36Sopenharmony_ci */ 182862306a36Sopenharmony_ci s->busy = file; 182962306a36Sopenharmony_ci ret = s->do_cmd(dev, s); 183062306a36Sopenharmony_ci if (ret == 0) 183162306a36Sopenharmony_ci return 0; 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_cicleanup: 183462306a36Sopenharmony_ci do_become_nonbusy(dev, s); 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci return ret; 183762306a36Sopenharmony_ci} 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci/* 184062306a36Sopenharmony_ci * COMEDI_CMDTEST ioctl 184162306a36Sopenharmony_ci * asynchronous acquisition command testing 184262306a36Sopenharmony_ci * 184362306a36Sopenharmony_ci * arg: 184462306a36Sopenharmony_ci * pointer to comedi_cmd structure 184562306a36Sopenharmony_ci * 184662306a36Sopenharmony_ci * reads: 184762306a36Sopenharmony_ci * comedi_cmd structure 184862306a36Sopenharmony_ci * channel/range list from cmd->chanlist pointer 184962306a36Sopenharmony_ci * 185062306a36Sopenharmony_ci * writes: 185162306a36Sopenharmony_ci * possibly modified comedi_cmd structure 185262306a36Sopenharmony_ci */ 185362306a36Sopenharmony_cistatic int do_cmdtest_ioctl(struct comedi_device *dev, 185462306a36Sopenharmony_ci struct comedi_cmd *cmd, bool *copy, void *file) 185562306a36Sopenharmony_ci{ 185662306a36Sopenharmony_ci struct comedi_subdevice *s; 185762306a36Sopenharmony_ci unsigned int __user *user_chanlist; 185862306a36Sopenharmony_ci int ret; 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci /* do some simple cmd validation */ 186362306a36Sopenharmony_ci ret = __comedi_get_user_cmd(dev, cmd); 186462306a36Sopenharmony_ci if (ret) 186562306a36Sopenharmony_ci return ret; 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci /* save user's chanlist pointer so it can be restored later */ 186862306a36Sopenharmony_ci user_chanlist = (unsigned int __user *)cmd->chanlist; 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci s = &dev->subdevices[cmd->subdev]; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci /* user_chanlist can be NULL for COMEDI_CMDTEST ioctl */ 187362306a36Sopenharmony_ci if (user_chanlist) { 187462306a36Sopenharmony_ci /* load channel/gain list */ 187562306a36Sopenharmony_ci ret = __comedi_get_user_chanlist(dev, s, user_chanlist, cmd); 187662306a36Sopenharmony_ci if (ret) 187762306a36Sopenharmony_ci return ret; 187862306a36Sopenharmony_ci } 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci ret = s->do_cmdtest(dev, s, cmd); 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci kfree(cmd->chanlist); /* free kernel copy of user chanlist */ 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci /* restore chanlist pointer before copying back */ 188562306a36Sopenharmony_ci cmd->chanlist = (unsigned int __force *)user_chanlist; 188662306a36Sopenharmony_ci *copy = true; 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci return ret; 188962306a36Sopenharmony_ci} 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci/* 189262306a36Sopenharmony_ci * COMEDI_LOCK ioctl 189362306a36Sopenharmony_ci * lock subdevice 189462306a36Sopenharmony_ci * 189562306a36Sopenharmony_ci * arg: 189662306a36Sopenharmony_ci * subdevice number 189762306a36Sopenharmony_ci * 189862306a36Sopenharmony_ci * reads: 189962306a36Sopenharmony_ci * nothing 190062306a36Sopenharmony_ci * 190162306a36Sopenharmony_ci * writes: 190262306a36Sopenharmony_ci * nothing 190362306a36Sopenharmony_ci */ 190462306a36Sopenharmony_cistatic int do_lock_ioctl(struct comedi_device *dev, unsigned long arg, 190562306a36Sopenharmony_ci void *file) 190662306a36Sopenharmony_ci{ 190762306a36Sopenharmony_ci int ret = 0; 190862306a36Sopenharmony_ci unsigned long flags; 190962306a36Sopenharmony_ci struct comedi_subdevice *s; 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 191262306a36Sopenharmony_ci if (arg >= dev->n_subdevices) 191362306a36Sopenharmony_ci return -EINVAL; 191462306a36Sopenharmony_ci s = &dev->subdevices[arg]; 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci spin_lock_irqsave(&s->spin_lock, flags); 191762306a36Sopenharmony_ci if (s->busy || s->lock) 191862306a36Sopenharmony_ci ret = -EBUSY; 191962306a36Sopenharmony_ci else 192062306a36Sopenharmony_ci s->lock = file; 192162306a36Sopenharmony_ci spin_unlock_irqrestore(&s->spin_lock, flags); 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci return ret; 192462306a36Sopenharmony_ci} 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci/* 192762306a36Sopenharmony_ci * COMEDI_UNLOCK ioctl 192862306a36Sopenharmony_ci * unlock subdevice 192962306a36Sopenharmony_ci * 193062306a36Sopenharmony_ci * arg: 193162306a36Sopenharmony_ci * subdevice number 193262306a36Sopenharmony_ci * 193362306a36Sopenharmony_ci * reads: 193462306a36Sopenharmony_ci * nothing 193562306a36Sopenharmony_ci * 193662306a36Sopenharmony_ci * writes: 193762306a36Sopenharmony_ci * nothing 193862306a36Sopenharmony_ci */ 193962306a36Sopenharmony_cistatic int do_unlock_ioctl(struct comedi_device *dev, unsigned long arg, 194062306a36Sopenharmony_ci void *file) 194162306a36Sopenharmony_ci{ 194262306a36Sopenharmony_ci struct comedi_subdevice *s; 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 194562306a36Sopenharmony_ci if (arg >= dev->n_subdevices) 194662306a36Sopenharmony_ci return -EINVAL; 194762306a36Sopenharmony_ci s = &dev->subdevices[arg]; 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci if (s->busy) 195062306a36Sopenharmony_ci return -EBUSY; 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci if (s->lock && s->lock != file) 195362306a36Sopenharmony_ci return -EACCES; 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci if (s->lock == file) 195662306a36Sopenharmony_ci s->lock = NULL; 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci return 0; 195962306a36Sopenharmony_ci} 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci/* 196262306a36Sopenharmony_ci * COMEDI_CANCEL ioctl 196362306a36Sopenharmony_ci * cancel asynchronous acquisition 196462306a36Sopenharmony_ci * 196562306a36Sopenharmony_ci * arg: 196662306a36Sopenharmony_ci * subdevice number 196762306a36Sopenharmony_ci * 196862306a36Sopenharmony_ci * reads: 196962306a36Sopenharmony_ci * nothing 197062306a36Sopenharmony_ci * 197162306a36Sopenharmony_ci * writes: 197262306a36Sopenharmony_ci * nothing 197362306a36Sopenharmony_ci */ 197462306a36Sopenharmony_cistatic int do_cancel_ioctl(struct comedi_device *dev, unsigned long arg, 197562306a36Sopenharmony_ci void *file) 197662306a36Sopenharmony_ci{ 197762306a36Sopenharmony_ci struct comedi_subdevice *s; 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 198062306a36Sopenharmony_ci if (arg >= dev->n_subdevices) 198162306a36Sopenharmony_ci return -EINVAL; 198262306a36Sopenharmony_ci s = &dev->subdevices[arg]; 198362306a36Sopenharmony_ci if (!s->async) 198462306a36Sopenharmony_ci return -EINVAL; 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci if (!s->busy) 198762306a36Sopenharmony_ci return 0; 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci if (s->busy != file) 199062306a36Sopenharmony_ci return -EBUSY; 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_ci return do_cancel(dev, s); 199362306a36Sopenharmony_ci} 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci/* 199662306a36Sopenharmony_ci * COMEDI_POLL ioctl 199762306a36Sopenharmony_ci * instructs driver to synchronize buffers 199862306a36Sopenharmony_ci * 199962306a36Sopenharmony_ci * arg: 200062306a36Sopenharmony_ci * subdevice number 200162306a36Sopenharmony_ci * 200262306a36Sopenharmony_ci * reads: 200362306a36Sopenharmony_ci * nothing 200462306a36Sopenharmony_ci * 200562306a36Sopenharmony_ci * writes: 200662306a36Sopenharmony_ci * nothing 200762306a36Sopenharmony_ci */ 200862306a36Sopenharmony_cistatic int do_poll_ioctl(struct comedi_device *dev, unsigned long arg, 200962306a36Sopenharmony_ci void *file) 201062306a36Sopenharmony_ci{ 201162306a36Sopenharmony_ci struct comedi_subdevice *s; 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 201462306a36Sopenharmony_ci if (arg >= dev->n_subdevices) 201562306a36Sopenharmony_ci return -EINVAL; 201662306a36Sopenharmony_ci s = &dev->subdevices[arg]; 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci if (!s->busy) 201962306a36Sopenharmony_ci return 0; 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci if (s->busy != file) 202262306a36Sopenharmony_ci return -EBUSY; 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci if (s->poll) 202562306a36Sopenharmony_ci return s->poll(dev, s); 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci return -EINVAL; 202862306a36Sopenharmony_ci} 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci/* 203162306a36Sopenharmony_ci * COMEDI_SETRSUBD ioctl 203262306a36Sopenharmony_ci * sets the current "read" subdevice on a per-file basis 203362306a36Sopenharmony_ci * 203462306a36Sopenharmony_ci * arg: 203562306a36Sopenharmony_ci * subdevice number 203662306a36Sopenharmony_ci * 203762306a36Sopenharmony_ci * reads: 203862306a36Sopenharmony_ci * nothing 203962306a36Sopenharmony_ci * 204062306a36Sopenharmony_ci * writes: 204162306a36Sopenharmony_ci * nothing 204262306a36Sopenharmony_ci */ 204362306a36Sopenharmony_cistatic int do_setrsubd_ioctl(struct comedi_device *dev, unsigned long arg, 204462306a36Sopenharmony_ci struct file *file) 204562306a36Sopenharmony_ci{ 204662306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 204762306a36Sopenharmony_ci struct comedi_subdevice *s_old, *s_new; 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 205062306a36Sopenharmony_ci if (arg >= dev->n_subdevices) 205162306a36Sopenharmony_ci return -EINVAL; 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci s_new = &dev->subdevices[arg]; 205462306a36Sopenharmony_ci s_old = comedi_file_read_subdevice(file); 205562306a36Sopenharmony_ci if (s_old == s_new) 205662306a36Sopenharmony_ci return 0; /* no change */ 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci if (!(s_new->subdev_flags & SDF_CMD_READ)) 205962306a36Sopenharmony_ci return -EINVAL; 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_ci /* 206262306a36Sopenharmony_ci * Check the file isn't still busy handling a "read" command on the 206362306a36Sopenharmony_ci * old subdevice (if any). 206462306a36Sopenharmony_ci */ 206562306a36Sopenharmony_ci if (s_old && s_old->busy == file && s_old->async && 206662306a36Sopenharmony_ci !(s_old->async->cmd.flags & CMDF_WRITE)) 206762306a36Sopenharmony_ci return -EBUSY; 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci WRITE_ONCE(cfp->read_subdev, s_new); 207062306a36Sopenharmony_ci return 0; 207162306a36Sopenharmony_ci} 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci/* 207462306a36Sopenharmony_ci * COMEDI_SETWSUBD ioctl 207562306a36Sopenharmony_ci * sets the current "write" subdevice on a per-file basis 207662306a36Sopenharmony_ci * 207762306a36Sopenharmony_ci * arg: 207862306a36Sopenharmony_ci * subdevice number 207962306a36Sopenharmony_ci * 208062306a36Sopenharmony_ci * reads: 208162306a36Sopenharmony_ci * nothing 208262306a36Sopenharmony_ci * 208362306a36Sopenharmony_ci * writes: 208462306a36Sopenharmony_ci * nothing 208562306a36Sopenharmony_ci */ 208662306a36Sopenharmony_cistatic int do_setwsubd_ioctl(struct comedi_device *dev, unsigned long arg, 208762306a36Sopenharmony_ci struct file *file) 208862306a36Sopenharmony_ci{ 208962306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 209062306a36Sopenharmony_ci struct comedi_subdevice *s_old, *s_new; 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 209362306a36Sopenharmony_ci if (arg >= dev->n_subdevices) 209462306a36Sopenharmony_ci return -EINVAL; 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci s_new = &dev->subdevices[arg]; 209762306a36Sopenharmony_ci s_old = comedi_file_write_subdevice(file); 209862306a36Sopenharmony_ci if (s_old == s_new) 209962306a36Sopenharmony_ci return 0; /* no change */ 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci if (!(s_new->subdev_flags & SDF_CMD_WRITE)) 210262306a36Sopenharmony_ci return -EINVAL; 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci /* 210562306a36Sopenharmony_ci * Check the file isn't still busy handling a "write" command on the 210662306a36Sopenharmony_ci * old subdevice (if any). 210762306a36Sopenharmony_ci */ 210862306a36Sopenharmony_ci if (s_old && s_old->busy == file && s_old->async && 210962306a36Sopenharmony_ci (s_old->async->cmd.flags & CMDF_WRITE)) 211062306a36Sopenharmony_ci return -EBUSY; 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci WRITE_ONCE(cfp->write_subdev, s_new); 211362306a36Sopenharmony_ci return 0; 211462306a36Sopenharmony_ci} 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_cistatic long comedi_unlocked_ioctl(struct file *file, unsigned int cmd, 211762306a36Sopenharmony_ci unsigned long arg) 211862306a36Sopenharmony_ci{ 211962306a36Sopenharmony_ci unsigned int minor = iminor(file_inode(file)); 212062306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 212162306a36Sopenharmony_ci struct comedi_device *dev = cfp->dev; 212262306a36Sopenharmony_ci int rc; 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci mutex_lock(&dev->mutex); 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci /* 212762306a36Sopenharmony_ci * Device config is special, because it must work on 212862306a36Sopenharmony_ci * an unconfigured device. 212962306a36Sopenharmony_ci */ 213062306a36Sopenharmony_ci if (cmd == COMEDI_DEVCONFIG) { 213162306a36Sopenharmony_ci if (minor >= COMEDI_NUM_BOARD_MINORS) { 213262306a36Sopenharmony_ci /* Device config not appropriate on non-board minors. */ 213362306a36Sopenharmony_ci rc = -ENOTTY; 213462306a36Sopenharmony_ci goto done; 213562306a36Sopenharmony_ci } 213662306a36Sopenharmony_ci rc = do_devconfig_ioctl(dev, 213762306a36Sopenharmony_ci (struct comedi_devconfig __user *)arg); 213862306a36Sopenharmony_ci if (rc == 0) { 213962306a36Sopenharmony_ci if (arg == 0 && 214062306a36Sopenharmony_ci dev->minor >= comedi_num_legacy_minors) { 214162306a36Sopenharmony_ci /* 214262306a36Sopenharmony_ci * Successfully unconfigured a dynamically 214362306a36Sopenharmony_ci * allocated device. Try and remove it. 214462306a36Sopenharmony_ci */ 214562306a36Sopenharmony_ci if (comedi_clear_board_dev(dev)) { 214662306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 214762306a36Sopenharmony_ci comedi_free_board_dev(dev); 214862306a36Sopenharmony_ci return rc; 214962306a36Sopenharmony_ci } 215062306a36Sopenharmony_ci } 215162306a36Sopenharmony_ci } 215262306a36Sopenharmony_ci goto done; 215362306a36Sopenharmony_ci } 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci if (!dev->attached) { 215662306a36Sopenharmony_ci dev_dbg(dev->class_dev, "no driver attached\n"); 215762306a36Sopenharmony_ci rc = -ENODEV; 215862306a36Sopenharmony_ci goto done; 215962306a36Sopenharmony_ci } 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci switch (cmd) { 216262306a36Sopenharmony_ci case COMEDI_BUFCONFIG: 216362306a36Sopenharmony_ci rc = do_bufconfig_ioctl(dev, 216462306a36Sopenharmony_ci (struct comedi_bufconfig __user *)arg); 216562306a36Sopenharmony_ci break; 216662306a36Sopenharmony_ci case COMEDI_DEVINFO: 216762306a36Sopenharmony_ci rc = do_devinfo_ioctl(dev, (struct comedi_devinfo __user *)arg, 216862306a36Sopenharmony_ci file); 216962306a36Sopenharmony_ci break; 217062306a36Sopenharmony_ci case COMEDI_SUBDINFO: 217162306a36Sopenharmony_ci rc = do_subdinfo_ioctl(dev, 217262306a36Sopenharmony_ci (struct comedi_subdinfo __user *)arg, 217362306a36Sopenharmony_ci file); 217462306a36Sopenharmony_ci break; 217562306a36Sopenharmony_ci case COMEDI_CHANINFO: { 217662306a36Sopenharmony_ci struct comedi_chaninfo it; 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_ci if (copy_from_user(&it, (void __user *)arg, sizeof(it))) 217962306a36Sopenharmony_ci rc = -EFAULT; 218062306a36Sopenharmony_ci else 218162306a36Sopenharmony_ci rc = do_chaninfo_ioctl(dev, &it); 218262306a36Sopenharmony_ci break; 218362306a36Sopenharmony_ci } 218462306a36Sopenharmony_ci case COMEDI_RANGEINFO: { 218562306a36Sopenharmony_ci struct comedi_rangeinfo it; 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci if (copy_from_user(&it, (void __user *)arg, sizeof(it))) 218862306a36Sopenharmony_ci rc = -EFAULT; 218962306a36Sopenharmony_ci else 219062306a36Sopenharmony_ci rc = do_rangeinfo_ioctl(dev, &it); 219162306a36Sopenharmony_ci break; 219262306a36Sopenharmony_ci } 219362306a36Sopenharmony_ci case COMEDI_BUFINFO: 219462306a36Sopenharmony_ci rc = do_bufinfo_ioctl(dev, 219562306a36Sopenharmony_ci (struct comedi_bufinfo __user *)arg, 219662306a36Sopenharmony_ci file); 219762306a36Sopenharmony_ci break; 219862306a36Sopenharmony_ci case COMEDI_LOCK: 219962306a36Sopenharmony_ci rc = do_lock_ioctl(dev, arg, file); 220062306a36Sopenharmony_ci break; 220162306a36Sopenharmony_ci case COMEDI_UNLOCK: 220262306a36Sopenharmony_ci rc = do_unlock_ioctl(dev, arg, file); 220362306a36Sopenharmony_ci break; 220462306a36Sopenharmony_ci case COMEDI_CANCEL: 220562306a36Sopenharmony_ci rc = do_cancel_ioctl(dev, arg, file); 220662306a36Sopenharmony_ci break; 220762306a36Sopenharmony_ci case COMEDI_CMD: { 220862306a36Sopenharmony_ci struct comedi_cmd cmd; 220962306a36Sopenharmony_ci bool copy = false; 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd))) { 221262306a36Sopenharmony_ci rc = -EFAULT; 221362306a36Sopenharmony_ci break; 221462306a36Sopenharmony_ci } 221562306a36Sopenharmony_ci rc = do_cmd_ioctl(dev, &cmd, ©, file); 221662306a36Sopenharmony_ci if (copy && copy_to_user((void __user *)arg, &cmd, sizeof(cmd))) 221762306a36Sopenharmony_ci rc = -EFAULT; 221862306a36Sopenharmony_ci break; 221962306a36Sopenharmony_ci } 222062306a36Sopenharmony_ci case COMEDI_CMDTEST: { 222162306a36Sopenharmony_ci struct comedi_cmd cmd; 222262306a36Sopenharmony_ci bool copy = false; 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd))) { 222562306a36Sopenharmony_ci rc = -EFAULT; 222662306a36Sopenharmony_ci break; 222762306a36Sopenharmony_ci } 222862306a36Sopenharmony_ci rc = do_cmdtest_ioctl(dev, &cmd, ©, file); 222962306a36Sopenharmony_ci if (copy && copy_to_user((void __user *)arg, &cmd, sizeof(cmd))) 223062306a36Sopenharmony_ci rc = -EFAULT; 223162306a36Sopenharmony_ci break; 223262306a36Sopenharmony_ci } 223362306a36Sopenharmony_ci case COMEDI_INSNLIST: { 223462306a36Sopenharmony_ci struct comedi_insnlist insnlist; 223562306a36Sopenharmony_ci struct comedi_insn *insns = NULL; 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci if (copy_from_user(&insnlist, (void __user *)arg, 223862306a36Sopenharmony_ci sizeof(insnlist))) { 223962306a36Sopenharmony_ci rc = -EFAULT; 224062306a36Sopenharmony_ci break; 224162306a36Sopenharmony_ci } 224262306a36Sopenharmony_ci insns = kcalloc(insnlist.n_insns, sizeof(*insns), GFP_KERNEL); 224362306a36Sopenharmony_ci if (!insns) { 224462306a36Sopenharmony_ci rc = -ENOMEM; 224562306a36Sopenharmony_ci break; 224662306a36Sopenharmony_ci } 224762306a36Sopenharmony_ci if (copy_from_user(insns, insnlist.insns, 224862306a36Sopenharmony_ci sizeof(*insns) * insnlist.n_insns)) { 224962306a36Sopenharmony_ci rc = -EFAULT; 225062306a36Sopenharmony_ci kfree(insns); 225162306a36Sopenharmony_ci break; 225262306a36Sopenharmony_ci } 225362306a36Sopenharmony_ci rc = do_insnlist_ioctl(dev, insns, insnlist.n_insns, file); 225462306a36Sopenharmony_ci kfree(insns); 225562306a36Sopenharmony_ci break; 225662306a36Sopenharmony_ci } 225762306a36Sopenharmony_ci case COMEDI_INSN: { 225862306a36Sopenharmony_ci struct comedi_insn insn; 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_ci if (copy_from_user(&insn, (void __user *)arg, sizeof(insn))) 226162306a36Sopenharmony_ci rc = -EFAULT; 226262306a36Sopenharmony_ci else 226362306a36Sopenharmony_ci rc = do_insn_ioctl(dev, &insn, file); 226462306a36Sopenharmony_ci break; 226562306a36Sopenharmony_ci } 226662306a36Sopenharmony_ci case COMEDI_POLL: 226762306a36Sopenharmony_ci rc = do_poll_ioctl(dev, arg, file); 226862306a36Sopenharmony_ci break; 226962306a36Sopenharmony_ci case COMEDI_SETRSUBD: 227062306a36Sopenharmony_ci rc = do_setrsubd_ioctl(dev, arg, file); 227162306a36Sopenharmony_ci break; 227262306a36Sopenharmony_ci case COMEDI_SETWSUBD: 227362306a36Sopenharmony_ci rc = do_setwsubd_ioctl(dev, arg, file); 227462306a36Sopenharmony_ci break; 227562306a36Sopenharmony_ci default: 227662306a36Sopenharmony_ci rc = -ENOTTY; 227762306a36Sopenharmony_ci break; 227862306a36Sopenharmony_ci } 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_cidone: 228162306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 228262306a36Sopenharmony_ci return rc; 228362306a36Sopenharmony_ci} 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_cistatic void comedi_vm_open(struct vm_area_struct *area) 228662306a36Sopenharmony_ci{ 228762306a36Sopenharmony_ci struct comedi_buf_map *bm; 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci bm = area->vm_private_data; 229062306a36Sopenharmony_ci comedi_buf_map_get(bm); 229162306a36Sopenharmony_ci} 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_cistatic void comedi_vm_close(struct vm_area_struct *area) 229462306a36Sopenharmony_ci{ 229562306a36Sopenharmony_ci struct comedi_buf_map *bm; 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_ci bm = area->vm_private_data; 229862306a36Sopenharmony_ci comedi_buf_map_put(bm); 229962306a36Sopenharmony_ci} 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_cistatic int comedi_vm_access(struct vm_area_struct *vma, unsigned long addr, 230262306a36Sopenharmony_ci void *buf, int len, int write) 230362306a36Sopenharmony_ci{ 230462306a36Sopenharmony_ci struct comedi_buf_map *bm = vma->vm_private_data; 230562306a36Sopenharmony_ci unsigned long offset = 230662306a36Sopenharmony_ci addr - vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT); 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci if (len < 0) 230962306a36Sopenharmony_ci return -EINVAL; 231062306a36Sopenharmony_ci if (len > vma->vm_end - addr) 231162306a36Sopenharmony_ci len = vma->vm_end - addr; 231262306a36Sopenharmony_ci return comedi_buf_map_access(bm, offset, buf, len, write); 231362306a36Sopenharmony_ci} 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_cistatic const struct vm_operations_struct comedi_vm_ops = { 231662306a36Sopenharmony_ci .open = comedi_vm_open, 231762306a36Sopenharmony_ci .close = comedi_vm_close, 231862306a36Sopenharmony_ci .access = comedi_vm_access, 231962306a36Sopenharmony_ci}; 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_cistatic int comedi_mmap(struct file *file, struct vm_area_struct *vma) 232262306a36Sopenharmony_ci{ 232362306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 232462306a36Sopenharmony_ci struct comedi_device *dev = cfp->dev; 232562306a36Sopenharmony_ci struct comedi_subdevice *s; 232662306a36Sopenharmony_ci struct comedi_async *async; 232762306a36Sopenharmony_ci struct comedi_buf_map *bm = NULL; 232862306a36Sopenharmony_ci struct comedi_buf_page *buf; 232962306a36Sopenharmony_ci unsigned long start = vma->vm_start; 233062306a36Sopenharmony_ci unsigned long size; 233162306a36Sopenharmony_ci int n_pages; 233262306a36Sopenharmony_ci int i; 233362306a36Sopenharmony_ci int retval = 0; 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_ci /* 233662306a36Sopenharmony_ci * 'trylock' avoids circular dependency with current->mm->mmap_lock 233762306a36Sopenharmony_ci * and down-reading &dev->attach_lock should normally succeed without 233862306a36Sopenharmony_ci * contention unless the device is in the process of being attached 233962306a36Sopenharmony_ci * or detached. 234062306a36Sopenharmony_ci */ 234162306a36Sopenharmony_ci if (!down_read_trylock(&dev->attach_lock)) 234262306a36Sopenharmony_ci return -EAGAIN; 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci if (!dev->attached) { 234562306a36Sopenharmony_ci dev_dbg(dev->class_dev, "no driver attached\n"); 234662306a36Sopenharmony_ci retval = -ENODEV; 234762306a36Sopenharmony_ci goto done; 234862306a36Sopenharmony_ci } 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_ci if (vma->vm_flags & VM_WRITE) 235162306a36Sopenharmony_ci s = comedi_file_write_subdevice(file); 235262306a36Sopenharmony_ci else 235362306a36Sopenharmony_ci s = comedi_file_read_subdevice(file); 235462306a36Sopenharmony_ci if (!s) { 235562306a36Sopenharmony_ci retval = -EINVAL; 235662306a36Sopenharmony_ci goto done; 235762306a36Sopenharmony_ci } 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci async = s->async; 236062306a36Sopenharmony_ci if (!async) { 236162306a36Sopenharmony_ci retval = -EINVAL; 236262306a36Sopenharmony_ci goto done; 236362306a36Sopenharmony_ci } 236462306a36Sopenharmony_ci 236562306a36Sopenharmony_ci if (vma->vm_pgoff != 0) { 236662306a36Sopenharmony_ci dev_dbg(dev->class_dev, "mmap() offset must be 0.\n"); 236762306a36Sopenharmony_ci retval = -EINVAL; 236862306a36Sopenharmony_ci goto done; 236962306a36Sopenharmony_ci } 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_ci size = vma->vm_end - vma->vm_start; 237262306a36Sopenharmony_ci if (size > async->prealloc_bufsz) { 237362306a36Sopenharmony_ci retval = -EFAULT; 237462306a36Sopenharmony_ci goto done; 237562306a36Sopenharmony_ci } 237662306a36Sopenharmony_ci if (offset_in_page(size)) { 237762306a36Sopenharmony_ci retval = -EFAULT; 237862306a36Sopenharmony_ci goto done; 237962306a36Sopenharmony_ci } 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_ci n_pages = vma_pages(vma); 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci /* get reference to current buf map (if any) */ 238462306a36Sopenharmony_ci bm = comedi_buf_map_from_subdev_get(s); 238562306a36Sopenharmony_ci if (!bm || n_pages > bm->n_pages) { 238662306a36Sopenharmony_ci retval = -EINVAL; 238762306a36Sopenharmony_ci goto done; 238862306a36Sopenharmony_ci } 238962306a36Sopenharmony_ci if (bm->dma_dir != DMA_NONE) { 239062306a36Sopenharmony_ci /* 239162306a36Sopenharmony_ci * DMA buffer was allocated as a single block. 239262306a36Sopenharmony_ci * Address is in page_list[0]. 239362306a36Sopenharmony_ci */ 239462306a36Sopenharmony_ci buf = &bm->page_list[0]; 239562306a36Sopenharmony_ci retval = dma_mmap_coherent(bm->dma_hw_dev, vma, buf->virt_addr, 239662306a36Sopenharmony_ci buf->dma_addr, n_pages * PAGE_SIZE); 239762306a36Sopenharmony_ci } else { 239862306a36Sopenharmony_ci for (i = 0; i < n_pages; ++i) { 239962306a36Sopenharmony_ci unsigned long pfn; 240062306a36Sopenharmony_ci 240162306a36Sopenharmony_ci buf = &bm->page_list[i]; 240262306a36Sopenharmony_ci pfn = page_to_pfn(virt_to_page(buf->virt_addr)); 240362306a36Sopenharmony_ci retval = remap_pfn_range(vma, start, pfn, PAGE_SIZE, 240462306a36Sopenharmony_ci PAGE_SHARED); 240562306a36Sopenharmony_ci if (retval) 240662306a36Sopenharmony_ci break; 240762306a36Sopenharmony_ci 240862306a36Sopenharmony_ci start += PAGE_SIZE; 240962306a36Sopenharmony_ci } 241062306a36Sopenharmony_ci } 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci if (retval == 0) { 241362306a36Sopenharmony_ci vma->vm_ops = &comedi_vm_ops; 241462306a36Sopenharmony_ci vma->vm_private_data = bm; 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ci vma->vm_ops->open(vma); 241762306a36Sopenharmony_ci } 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_cidone: 242062306a36Sopenharmony_ci up_read(&dev->attach_lock); 242162306a36Sopenharmony_ci comedi_buf_map_put(bm); /* put reference to buf map - okay if NULL */ 242262306a36Sopenharmony_ci return retval; 242362306a36Sopenharmony_ci} 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_cistatic __poll_t comedi_poll(struct file *file, poll_table *wait) 242662306a36Sopenharmony_ci{ 242762306a36Sopenharmony_ci __poll_t mask = 0; 242862306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 242962306a36Sopenharmony_ci struct comedi_device *dev = cfp->dev; 243062306a36Sopenharmony_ci struct comedi_subdevice *s, *s_read; 243162306a36Sopenharmony_ci 243262306a36Sopenharmony_ci down_read(&dev->attach_lock); 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci if (!dev->attached) { 243562306a36Sopenharmony_ci dev_dbg(dev->class_dev, "no driver attached\n"); 243662306a36Sopenharmony_ci goto done; 243762306a36Sopenharmony_ci } 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_ci s = comedi_file_read_subdevice(file); 244062306a36Sopenharmony_ci s_read = s; 244162306a36Sopenharmony_ci if (s && s->async) { 244262306a36Sopenharmony_ci poll_wait(file, &s->async->wait_head, wait); 244362306a36Sopenharmony_ci if (s->busy != file || !comedi_is_subdevice_running(s) || 244462306a36Sopenharmony_ci (s->async->cmd.flags & CMDF_WRITE) || 244562306a36Sopenharmony_ci comedi_buf_read_n_available(s) > 0) 244662306a36Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 244762306a36Sopenharmony_ci } 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci s = comedi_file_write_subdevice(file); 245062306a36Sopenharmony_ci if (s && s->async) { 245162306a36Sopenharmony_ci unsigned int bps = comedi_bytes_per_sample(s); 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci if (s != s_read) 245462306a36Sopenharmony_ci poll_wait(file, &s->async->wait_head, wait); 245562306a36Sopenharmony_ci if (s->busy != file || !comedi_is_subdevice_running(s) || 245662306a36Sopenharmony_ci !(s->async->cmd.flags & CMDF_WRITE) || 245762306a36Sopenharmony_ci comedi_buf_write_n_available(s) >= bps) 245862306a36Sopenharmony_ci mask |= EPOLLOUT | EPOLLWRNORM; 245962306a36Sopenharmony_ci } 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_cidone: 246262306a36Sopenharmony_ci up_read(&dev->attach_lock); 246362306a36Sopenharmony_ci return mask; 246462306a36Sopenharmony_ci} 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_cistatic ssize_t comedi_write(struct file *file, const char __user *buf, 246762306a36Sopenharmony_ci size_t nbytes, loff_t *offset) 246862306a36Sopenharmony_ci{ 246962306a36Sopenharmony_ci struct comedi_subdevice *s; 247062306a36Sopenharmony_ci struct comedi_async *async; 247162306a36Sopenharmony_ci unsigned int n, m; 247262306a36Sopenharmony_ci ssize_t count = 0; 247362306a36Sopenharmony_ci int retval = 0; 247462306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 247562306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 247662306a36Sopenharmony_ci struct comedi_device *dev = cfp->dev; 247762306a36Sopenharmony_ci bool become_nonbusy = false; 247862306a36Sopenharmony_ci bool attach_locked; 247962306a36Sopenharmony_ci unsigned int old_detach_count; 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci /* Protect against device detachment during operation. */ 248262306a36Sopenharmony_ci down_read(&dev->attach_lock); 248362306a36Sopenharmony_ci attach_locked = true; 248462306a36Sopenharmony_ci old_detach_count = dev->detach_count; 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci if (!dev->attached) { 248762306a36Sopenharmony_ci dev_dbg(dev->class_dev, "no driver attached\n"); 248862306a36Sopenharmony_ci retval = -ENODEV; 248962306a36Sopenharmony_ci goto out; 249062306a36Sopenharmony_ci } 249162306a36Sopenharmony_ci 249262306a36Sopenharmony_ci s = comedi_file_write_subdevice(file); 249362306a36Sopenharmony_ci if (!s || !s->async) { 249462306a36Sopenharmony_ci retval = -EIO; 249562306a36Sopenharmony_ci goto out; 249662306a36Sopenharmony_ci } 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_ci async = s->async; 249962306a36Sopenharmony_ci if (s->busy != file || !(async->cmd.flags & CMDF_WRITE)) { 250062306a36Sopenharmony_ci retval = -EINVAL; 250162306a36Sopenharmony_ci goto out; 250262306a36Sopenharmony_ci } 250362306a36Sopenharmony_ci 250462306a36Sopenharmony_ci add_wait_queue(&async->wait_head, &wait); 250562306a36Sopenharmony_ci while (count == 0 && !retval) { 250662306a36Sopenharmony_ci unsigned int runflags; 250762306a36Sopenharmony_ci unsigned int wp, n1, n2; 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 251062306a36Sopenharmony_ci 251162306a36Sopenharmony_ci runflags = comedi_get_subdevice_runflags(s); 251262306a36Sopenharmony_ci if (!comedi_is_runflags_running(runflags)) { 251362306a36Sopenharmony_ci if (comedi_is_runflags_in_error(runflags)) 251462306a36Sopenharmony_ci retval = -EPIPE; 251562306a36Sopenharmony_ci if (retval || nbytes) 251662306a36Sopenharmony_ci become_nonbusy = true; 251762306a36Sopenharmony_ci break; 251862306a36Sopenharmony_ci } 251962306a36Sopenharmony_ci if (nbytes == 0) 252062306a36Sopenharmony_ci break; 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_ci /* Allocate all free buffer space. */ 252362306a36Sopenharmony_ci comedi_buf_write_alloc(s, async->prealloc_bufsz); 252462306a36Sopenharmony_ci m = comedi_buf_write_n_allocated(s); 252562306a36Sopenharmony_ci n = min_t(size_t, m, nbytes); 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_ci if (n == 0) { 252862306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 252962306a36Sopenharmony_ci retval = -EAGAIN; 253062306a36Sopenharmony_ci break; 253162306a36Sopenharmony_ci } 253262306a36Sopenharmony_ci schedule(); 253362306a36Sopenharmony_ci if (signal_pending(current)) { 253462306a36Sopenharmony_ci retval = -ERESTARTSYS; 253562306a36Sopenharmony_ci break; 253662306a36Sopenharmony_ci } 253762306a36Sopenharmony_ci if (s->busy != file || 253862306a36Sopenharmony_ci !(async->cmd.flags & CMDF_WRITE)) { 253962306a36Sopenharmony_ci retval = -EINVAL; 254062306a36Sopenharmony_ci break; 254162306a36Sopenharmony_ci } 254262306a36Sopenharmony_ci continue; 254362306a36Sopenharmony_ci } 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 254662306a36Sopenharmony_ci wp = async->buf_write_ptr; 254762306a36Sopenharmony_ci n1 = min(n, async->prealloc_bufsz - wp); 254862306a36Sopenharmony_ci n2 = n - n1; 254962306a36Sopenharmony_ci m = copy_from_user(async->prealloc_buf + wp, buf, n1); 255062306a36Sopenharmony_ci if (m) 255162306a36Sopenharmony_ci m += n2; 255262306a36Sopenharmony_ci else if (n2) 255362306a36Sopenharmony_ci m = copy_from_user(async->prealloc_buf, buf + n1, n2); 255462306a36Sopenharmony_ci if (m) { 255562306a36Sopenharmony_ci n -= m; 255662306a36Sopenharmony_ci retval = -EFAULT; 255762306a36Sopenharmony_ci } 255862306a36Sopenharmony_ci comedi_buf_write_free(s, n); 255962306a36Sopenharmony_ci 256062306a36Sopenharmony_ci count += n; 256162306a36Sopenharmony_ci nbytes -= n; 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_ci buf += n; 256462306a36Sopenharmony_ci } 256562306a36Sopenharmony_ci remove_wait_queue(&async->wait_head, &wait); 256662306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 256762306a36Sopenharmony_ci if (become_nonbusy && count == 0) { 256862306a36Sopenharmony_ci struct comedi_subdevice *new_s; 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci /* 257162306a36Sopenharmony_ci * To avoid deadlock, cannot acquire dev->mutex 257262306a36Sopenharmony_ci * while dev->attach_lock is held. 257362306a36Sopenharmony_ci */ 257462306a36Sopenharmony_ci up_read(&dev->attach_lock); 257562306a36Sopenharmony_ci attach_locked = false; 257662306a36Sopenharmony_ci mutex_lock(&dev->mutex); 257762306a36Sopenharmony_ci /* 257862306a36Sopenharmony_ci * Check device hasn't become detached behind our back. 257962306a36Sopenharmony_ci * Checking dev->detach_count is unchanged ought to be 258062306a36Sopenharmony_ci * sufficient (unless there have been 2**32 detaches in the 258162306a36Sopenharmony_ci * meantime!), but check the subdevice pointer as well just in 258262306a36Sopenharmony_ci * case. 258362306a36Sopenharmony_ci * 258462306a36Sopenharmony_ci * Also check the subdevice is still in a suitable state to 258562306a36Sopenharmony_ci * become non-busy in case it changed behind our back. 258662306a36Sopenharmony_ci */ 258762306a36Sopenharmony_ci new_s = comedi_file_write_subdevice(file); 258862306a36Sopenharmony_ci if (dev->attached && old_detach_count == dev->detach_count && 258962306a36Sopenharmony_ci s == new_s && new_s->async == async && s->busy == file && 259062306a36Sopenharmony_ci (async->cmd.flags & CMDF_WRITE) && 259162306a36Sopenharmony_ci !comedi_is_subdevice_running(s)) 259262306a36Sopenharmony_ci do_become_nonbusy(dev, s); 259362306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 259462306a36Sopenharmony_ci } 259562306a36Sopenharmony_ciout: 259662306a36Sopenharmony_ci if (attach_locked) 259762306a36Sopenharmony_ci up_read(&dev->attach_lock); 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_ci return count ? count : retval; 260062306a36Sopenharmony_ci} 260162306a36Sopenharmony_ci 260262306a36Sopenharmony_cistatic ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes, 260362306a36Sopenharmony_ci loff_t *offset) 260462306a36Sopenharmony_ci{ 260562306a36Sopenharmony_ci struct comedi_subdevice *s; 260662306a36Sopenharmony_ci struct comedi_async *async; 260762306a36Sopenharmony_ci unsigned int n, m; 260862306a36Sopenharmony_ci ssize_t count = 0; 260962306a36Sopenharmony_ci int retval = 0; 261062306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 261162306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 261262306a36Sopenharmony_ci struct comedi_device *dev = cfp->dev; 261362306a36Sopenharmony_ci unsigned int old_detach_count; 261462306a36Sopenharmony_ci bool become_nonbusy = false; 261562306a36Sopenharmony_ci bool attach_locked; 261662306a36Sopenharmony_ci 261762306a36Sopenharmony_ci /* Protect against device detachment during operation. */ 261862306a36Sopenharmony_ci down_read(&dev->attach_lock); 261962306a36Sopenharmony_ci attach_locked = true; 262062306a36Sopenharmony_ci old_detach_count = dev->detach_count; 262162306a36Sopenharmony_ci 262262306a36Sopenharmony_ci if (!dev->attached) { 262362306a36Sopenharmony_ci dev_dbg(dev->class_dev, "no driver attached\n"); 262462306a36Sopenharmony_ci retval = -ENODEV; 262562306a36Sopenharmony_ci goto out; 262662306a36Sopenharmony_ci } 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_ci s = comedi_file_read_subdevice(file); 262962306a36Sopenharmony_ci if (!s || !s->async) { 263062306a36Sopenharmony_ci retval = -EIO; 263162306a36Sopenharmony_ci goto out; 263262306a36Sopenharmony_ci } 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_ci async = s->async; 263562306a36Sopenharmony_ci if (s->busy != file || (async->cmd.flags & CMDF_WRITE)) { 263662306a36Sopenharmony_ci retval = -EINVAL; 263762306a36Sopenharmony_ci goto out; 263862306a36Sopenharmony_ci } 263962306a36Sopenharmony_ci 264062306a36Sopenharmony_ci add_wait_queue(&async->wait_head, &wait); 264162306a36Sopenharmony_ci while (count == 0 && !retval) { 264262306a36Sopenharmony_ci unsigned int rp, n1, n2; 264362306a36Sopenharmony_ci 264462306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_ci m = comedi_buf_read_n_available(s); 264762306a36Sopenharmony_ci n = min_t(size_t, m, nbytes); 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_ci if (n == 0) { 265062306a36Sopenharmony_ci unsigned int runflags = 265162306a36Sopenharmony_ci comedi_get_subdevice_runflags(s); 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_ci if (!comedi_is_runflags_running(runflags)) { 265462306a36Sopenharmony_ci if (comedi_is_runflags_in_error(runflags)) 265562306a36Sopenharmony_ci retval = -EPIPE; 265662306a36Sopenharmony_ci if (retval || nbytes) 265762306a36Sopenharmony_ci become_nonbusy = true; 265862306a36Sopenharmony_ci break; 265962306a36Sopenharmony_ci } 266062306a36Sopenharmony_ci if (nbytes == 0) 266162306a36Sopenharmony_ci break; 266262306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 266362306a36Sopenharmony_ci retval = -EAGAIN; 266462306a36Sopenharmony_ci break; 266562306a36Sopenharmony_ci } 266662306a36Sopenharmony_ci schedule(); 266762306a36Sopenharmony_ci if (signal_pending(current)) { 266862306a36Sopenharmony_ci retval = -ERESTARTSYS; 266962306a36Sopenharmony_ci break; 267062306a36Sopenharmony_ci } 267162306a36Sopenharmony_ci if (s->busy != file || 267262306a36Sopenharmony_ci (async->cmd.flags & CMDF_WRITE)) { 267362306a36Sopenharmony_ci retval = -EINVAL; 267462306a36Sopenharmony_ci break; 267562306a36Sopenharmony_ci } 267662306a36Sopenharmony_ci continue; 267762306a36Sopenharmony_ci } 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 268062306a36Sopenharmony_ci rp = async->buf_read_ptr; 268162306a36Sopenharmony_ci n1 = min(n, async->prealloc_bufsz - rp); 268262306a36Sopenharmony_ci n2 = n - n1; 268362306a36Sopenharmony_ci m = copy_to_user(buf, async->prealloc_buf + rp, n1); 268462306a36Sopenharmony_ci if (m) 268562306a36Sopenharmony_ci m += n2; 268662306a36Sopenharmony_ci else if (n2) 268762306a36Sopenharmony_ci m = copy_to_user(buf + n1, async->prealloc_buf, n2); 268862306a36Sopenharmony_ci if (m) { 268962306a36Sopenharmony_ci n -= m; 269062306a36Sopenharmony_ci retval = -EFAULT; 269162306a36Sopenharmony_ci } 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ci comedi_buf_read_alloc(s, n); 269462306a36Sopenharmony_ci comedi_buf_read_free(s, n); 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci count += n; 269762306a36Sopenharmony_ci nbytes -= n; 269862306a36Sopenharmony_ci 269962306a36Sopenharmony_ci buf += n; 270062306a36Sopenharmony_ci } 270162306a36Sopenharmony_ci remove_wait_queue(&async->wait_head, &wait); 270262306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 270362306a36Sopenharmony_ci if (become_nonbusy && count == 0) { 270462306a36Sopenharmony_ci struct comedi_subdevice *new_s; 270562306a36Sopenharmony_ci 270662306a36Sopenharmony_ci /* 270762306a36Sopenharmony_ci * To avoid deadlock, cannot acquire dev->mutex 270862306a36Sopenharmony_ci * while dev->attach_lock is held. 270962306a36Sopenharmony_ci */ 271062306a36Sopenharmony_ci up_read(&dev->attach_lock); 271162306a36Sopenharmony_ci attach_locked = false; 271262306a36Sopenharmony_ci mutex_lock(&dev->mutex); 271362306a36Sopenharmony_ci /* 271462306a36Sopenharmony_ci * Check device hasn't become detached behind our back. 271562306a36Sopenharmony_ci * Checking dev->detach_count is unchanged ought to be 271662306a36Sopenharmony_ci * sufficient (unless there have been 2**32 detaches in the 271762306a36Sopenharmony_ci * meantime!), but check the subdevice pointer as well just in 271862306a36Sopenharmony_ci * case. 271962306a36Sopenharmony_ci * 272062306a36Sopenharmony_ci * Also check the subdevice is still in a suitable state to 272162306a36Sopenharmony_ci * become non-busy in case it changed behind our back. 272262306a36Sopenharmony_ci */ 272362306a36Sopenharmony_ci new_s = comedi_file_read_subdevice(file); 272462306a36Sopenharmony_ci if (dev->attached && old_detach_count == dev->detach_count && 272562306a36Sopenharmony_ci s == new_s && new_s->async == async && s->busy == file && 272662306a36Sopenharmony_ci !(async->cmd.flags & CMDF_WRITE) && 272762306a36Sopenharmony_ci !comedi_is_subdevice_running(s) && 272862306a36Sopenharmony_ci comedi_buf_read_n_available(s) == 0) 272962306a36Sopenharmony_ci do_become_nonbusy(dev, s); 273062306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 273162306a36Sopenharmony_ci } 273262306a36Sopenharmony_ciout: 273362306a36Sopenharmony_ci if (attach_locked) 273462306a36Sopenharmony_ci up_read(&dev->attach_lock); 273562306a36Sopenharmony_ci 273662306a36Sopenharmony_ci return count ? count : retval; 273762306a36Sopenharmony_ci} 273862306a36Sopenharmony_ci 273962306a36Sopenharmony_cistatic int comedi_open(struct inode *inode, struct file *file) 274062306a36Sopenharmony_ci{ 274162306a36Sopenharmony_ci const unsigned int minor = iminor(inode); 274262306a36Sopenharmony_ci struct comedi_file *cfp; 274362306a36Sopenharmony_ci struct comedi_device *dev = comedi_dev_get_from_minor(minor); 274462306a36Sopenharmony_ci int rc; 274562306a36Sopenharmony_ci 274662306a36Sopenharmony_ci if (!dev) { 274762306a36Sopenharmony_ci pr_debug("invalid minor number\n"); 274862306a36Sopenharmony_ci return -ENODEV; 274962306a36Sopenharmony_ci } 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_ci cfp = kzalloc(sizeof(*cfp), GFP_KERNEL); 275262306a36Sopenharmony_ci if (!cfp) { 275362306a36Sopenharmony_ci comedi_dev_put(dev); 275462306a36Sopenharmony_ci return -ENOMEM; 275562306a36Sopenharmony_ci } 275662306a36Sopenharmony_ci 275762306a36Sopenharmony_ci cfp->dev = dev; 275862306a36Sopenharmony_ci 275962306a36Sopenharmony_ci mutex_lock(&dev->mutex); 276062306a36Sopenharmony_ci if (!dev->attached && !capable(CAP_SYS_ADMIN)) { 276162306a36Sopenharmony_ci dev_dbg(dev->class_dev, "not attached and not CAP_SYS_ADMIN\n"); 276262306a36Sopenharmony_ci rc = -ENODEV; 276362306a36Sopenharmony_ci goto out; 276462306a36Sopenharmony_ci } 276562306a36Sopenharmony_ci if (dev->attached && dev->use_count == 0) { 276662306a36Sopenharmony_ci if (!try_module_get(dev->driver->module)) { 276762306a36Sopenharmony_ci rc = -ENXIO; 276862306a36Sopenharmony_ci goto out; 276962306a36Sopenharmony_ci } 277062306a36Sopenharmony_ci if (dev->open) { 277162306a36Sopenharmony_ci rc = dev->open(dev); 277262306a36Sopenharmony_ci if (rc < 0) { 277362306a36Sopenharmony_ci module_put(dev->driver->module); 277462306a36Sopenharmony_ci goto out; 277562306a36Sopenharmony_ci } 277662306a36Sopenharmony_ci } 277762306a36Sopenharmony_ci } 277862306a36Sopenharmony_ci 277962306a36Sopenharmony_ci dev->use_count++; 278062306a36Sopenharmony_ci file->private_data = cfp; 278162306a36Sopenharmony_ci comedi_file_reset(file); 278262306a36Sopenharmony_ci rc = 0; 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_ciout: 278562306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 278662306a36Sopenharmony_ci if (rc) { 278762306a36Sopenharmony_ci comedi_dev_put(dev); 278862306a36Sopenharmony_ci kfree(cfp); 278962306a36Sopenharmony_ci } 279062306a36Sopenharmony_ci return rc; 279162306a36Sopenharmony_ci} 279262306a36Sopenharmony_ci 279362306a36Sopenharmony_cistatic int comedi_fasync(int fd, struct file *file, int on) 279462306a36Sopenharmony_ci{ 279562306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 279662306a36Sopenharmony_ci struct comedi_device *dev = cfp->dev; 279762306a36Sopenharmony_ci 279862306a36Sopenharmony_ci return fasync_helper(fd, file, on, &dev->async_queue); 279962306a36Sopenharmony_ci} 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_cistatic int comedi_close(struct inode *inode, struct file *file) 280262306a36Sopenharmony_ci{ 280362306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 280462306a36Sopenharmony_ci struct comedi_device *dev = cfp->dev; 280562306a36Sopenharmony_ci struct comedi_subdevice *s = NULL; 280662306a36Sopenharmony_ci int i; 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci mutex_lock(&dev->mutex); 280962306a36Sopenharmony_ci 281062306a36Sopenharmony_ci if (dev->subdevices) { 281162306a36Sopenharmony_ci for (i = 0; i < dev->n_subdevices; i++) { 281262306a36Sopenharmony_ci s = &dev->subdevices[i]; 281362306a36Sopenharmony_ci 281462306a36Sopenharmony_ci if (s->busy == file) 281562306a36Sopenharmony_ci do_cancel(dev, s); 281662306a36Sopenharmony_ci if (s->lock == file) 281762306a36Sopenharmony_ci s->lock = NULL; 281862306a36Sopenharmony_ci } 281962306a36Sopenharmony_ci } 282062306a36Sopenharmony_ci if (dev->attached && dev->use_count == 1) { 282162306a36Sopenharmony_ci if (dev->close) 282262306a36Sopenharmony_ci dev->close(dev); 282362306a36Sopenharmony_ci module_put(dev->driver->module); 282462306a36Sopenharmony_ci } 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci dev->use_count--; 282762306a36Sopenharmony_ci 282862306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 282962306a36Sopenharmony_ci comedi_dev_put(dev); 283062306a36Sopenharmony_ci kfree(cfp); 283162306a36Sopenharmony_ci 283262306a36Sopenharmony_ci return 0; 283362306a36Sopenharmony_ci} 283462306a36Sopenharmony_ci 283562306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci#define COMEDI32_CHANINFO _IOR(CIO, 3, struct comedi32_chaninfo_struct) 283862306a36Sopenharmony_ci#define COMEDI32_RANGEINFO _IOR(CIO, 8, struct comedi32_rangeinfo_struct) 283962306a36Sopenharmony_ci/* 284062306a36Sopenharmony_ci * N.B. COMEDI32_CMD and COMEDI_CMD ought to use _IOWR, not _IOR. 284162306a36Sopenharmony_ci * It's too late to change it now, but it only affects the command number. 284262306a36Sopenharmony_ci */ 284362306a36Sopenharmony_ci#define COMEDI32_CMD _IOR(CIO, 9, struct comedi32_cmd_struct) 284462306a36Sopenharmony_ci/* 284562306a36Sopenharmony_ci * N.B. COMEDI32_CMDTEST and COMEDI_CMDTEST ought to use _IOWR, not _IOR. 284662306a36Sopenharmony_ci * It's too late to change it now, but it only affects the command number. 284762306a36Sopenharmony_ci */ 284862306a36Sopenharmony_ci#define COMEDI32_CMDTEST _IOR(CIO, 10, struct comedi32_cmd_struct) 284962306a36Sopenharmony_ci#define COMEDI32_INSNLIST _IOR(CIO, 11, struct comedi32_insnlist_struct) 285062306a36Sopenharmony_ci#define COMEDI32_INSN _IOR(CIO, 12, struct comedi32_insn_struct) 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_cistruct comedi32_chaninfo_struct { 285362306a36Sopenharmony_ci unsigned int subdev; 285462306a36Sopenharmony_ci compat_uptr_t maxdata_list; /* 32-bit 'unsigned int *' */ 285562306a36Sopenharmony_ci compat_uptr_t flaglist; /* 32-bit 'unsigned int *' */ 285662306a36Sopenharmony_ci compat_uptr_t rangelist; /* 32-bit 'unsigned int *' */ 285762306a36Sopenharmony_ci unsigned int unused[4]; 285862306a36Sopenharmony_ci}; 285962306a36Sopenharmony_ci 286062306a36Sopenharmony_cistruct comedi32_rangeinfo_struct { 286162306a36Sopenharmony_ci unsigned int range_type; 286262306a36Sopenharmony_ci compat_uptr_t range_ptr; /* 32-bit 'void *' */ 286362306a36Sopenharmony_ci}; 286462306a36Sopenharmony_ci 286562306a36Sopenharmony_cistruct comedi32_cmd_struct { 286662306a36Sopenharmony_ci unsigned int subdev; 286762306a36Sopenharmony_ci unsigned int flags; 286862306a36Sopenharmony_ci unsigned int start_src; 286962306a36Sopenharmony_ci unsigned int start_arg; 287062306a36Sopenharmony_ci unsigned int scan_begin_src; 287162306a36Sopenharmony_ci unsigned int scan_begin_arg; 287262306a36Sopenharmony_ci unsigned int convert_src; 287362306a36Sopenharmony_ci unsigned int convert_arg; 287462306a36Sopenharmony_ci unsigned int scan_end_src; 287562306a36Sopenharmony_ci unsigned int scan_end_arg; 287662306a36Sopenharmony_ci unsigned int stop_src; 287762306a36Sopenharmony_ci unsigned int stop_arg; 287862306a36Sopenharmony_ci compat_uptr_t chanlist; /* 32-bit 'unsigned int *' */ 287962306a36Sopenharmony_ci unsigned int chanlist_len; 288062306a36Sopenharmony_ci compat_uptr_t data; /* 32-bit 'short *' */ 288162306a36Sopenharmony_ci unsigned int data_len; 288262306a36Sopenharmony_ci}; 288362306a36Sopenharmony_ci 288462306a36Sopenharmony_cistruct comedi32_insn_struct { 288562306a36Sopenharmony_ci unsigned int insn; 288662306a36Sopenharmony_ci unsigned int n; 288762306a36Sopenharmony_ci compat_uptr_t data; /* 32-bit 'unsigned int *' */ 288862306a36Sopenharmony_ci unsigned int subdev; 288962306a36Sopenharmony_ci unsigned int chanspec; 289062306a36Sopenharmony_ci unsigned int unused[3]; 289162306a36Sopenharmony_ci}; 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_cistruct comedi32_insnlist_struct { 289462306a36Sopenharmony_ci unsigned int n_insns; 289562306a36Sopenharmony_ci compat_uptr_t insns; /* 32-bit 'struct comedi_insn *' */ 289662306a36Sopenharmony_ci}; 289762306a36Sopenharmony_ci 289862306a36Sopenharmony_ci/* Handle 32-bit COMEDI_CHANINFO ioctl. */ 289962306a36Sopenharmony_cistatic int compat_chaninfo(struct file *file, unsigned long arg) 290062306a36Sopenharmony_ci{ 290162306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 290262306a36Sopenharmony_ci struct comedi_device *dev = cfp->dev; 290362306a36Sopenharmony_ci struct comedi32_chaninfo_struct chaninfo32; 290462306a36Sopenharmony_ci struct comedi_chaninfo chaninfo; 290562306a36Sopenharmony_ci int err; 290662306a36Sopenharmony_ci 290762306a36Sopenharmony_ci if (copy_from_user(&chaninfo32, compat_ptr(arg), sizeof(chaninfo32))) 290862306a36Sopenharmony_ci return -EFAULT; 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_ci memset(&chaninfo, 0, sizeof(chaninfo)); 291162306a36Sopenharmony_ci chaninfo.subdev = chaninfo32.subdev; 291262306a36Sopenharmony_ci chaninfo.maxdata_list = compat_ptr(chaninfo32.maxdata_list); 291362306a36Sopenharmony_ci chaninfo.flaglist = compat_ptr(chaninfo32.flaglist); 291462306a36Sopenharmony_ci chaninfo.rangelist = compat_ptr(chaninfo32.rangelist); 291562306a36Sopenharmony_ci 291662306a36Sopenharmony_ci mutex_lock(&dev->mutex); 291762306a36Sopenharmony_ci err = do_chaninfo_ioctl(dev, &chaninfo); 291862306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 291962306a36Sopenharmony_ci return err; 292062306a36Sopenharmony_ci} 292162306a36Sopenharmony_ci 292262306a36Sopenharmony_ci/* Handle 32-bit COMEDI_RANGEINFO ioctl. */ 292362306a36Sopenharmony_cistatic int compat_rangeinfo(struct file *file, unsigned long arg) 292462306a36Sopenharmony_ci{ 292562306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 292662306a36Sopenharmony_ci struct comedi_device *dev = cfp->dev; 292762306a36Sopenharmony_ci struct comedi32_rangeinfo_struct rangeinfo32; 292862306a36Sopenharmony_ci struct comedi_rangeinfo rangeinfo; 292962306a36Sopenharmony_ci int err; 293062306a36Sopenharmony_ci 293162306a36Sopenharmony_ci if (copy_from_user(&rangeinfo32, compat_ptr(arg), sizeof(rangeinfo32))) 293262306a36Sopenharmony_ci return -EFAULT; 293362306a36Sopenharmony_ci memset(&rangeinfo, 0, sizeof(rangeinfo)); 293462306a36Sopenharmony_ci rangeinfo.range_type = rangeinfo32.range_type; 293562306a36Sopenharmony_ci rangeinfo.range_ptr = compat_ptr(rangeinfo32.range_ptr); 293662306a36Sopenharmony_ci 293762306a36Sopenharmony_ci mutex_lock(&dev->mutex); 293862306a36Sopenharmony_ci err = do_rangeinfo_ioctl(dev, &rangeinfo); 293962306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 294062306a36Sopenharmony_ci return err; 294162306a36Sopenharmony_ci} 294262306a36Sopenharmony_ci 294362306a36Sopenharmony_ci/* Copy 32-bit cmd structure to native cmd structure. */ 294462306a36Sopenharmony_cistatic int get_compat_cmd(struct comedi_cmd *cmd, 294562306a36Sopenharmony_ci struct comedi32_cmd_struct __user *cmd32) 294662306a36Sopenharmony_ci{ 294762306a36Sopenharmony_ci struct comedi32_cmd_struct v32; 294862306a36Sopenharmony_ci 294962306a36Sopenharmony_ci if (copy_from_user(&v32, cmd32, sizeof(v32))) 295062306a36Sopenharmony_ci return -EFAULT; 295162306a36Sopenharmony_ci 295262306a36Sopenharmony_ci cmd->subdev = v32.subdev; 295362306a36Sopenharmony_ci cmd->flags = v32.flags; 295462306a36Sopenharmony_ci cmd->start_src = v32.start_src; 295562306a36Sopenharmony_ci cmd->start_arg = v32.start_arg; 295662306a36Sopenharmony_ci cmd->scan_begin_src = v32.scan_begin_src; 295762306a36Sopenharmony_ci cmd->scan_begin_arg = v32.scan_begin_arg; 295862306a36Sopenharmony_ci cmd->convert_src = v32.convert_src; 295962306a36Sopenharmony_ci cmd->convert_arg = v32.convert_arg; 296062306a36Sopenharmony_ci cmd->scan_end_src = v32.scan_end_src; 296162306a36Sopenharmony_ci cmd->scan_end_arg = v32.scan_end_arg; 296262306a36Sopenharmony_ci cmd->stop_src = v32.stop_src; 296362306a36Sopenharmony_ci cmd->stop_arg = v32.stop_arg; 296462306a36Sopenharmony_ci cmd->chanlist = (unsigned int __force *)compat_ptr(v32.chanlist); 296562306a36Sopenharmony_ci cmd->chanlist_len = v32.chanlist_len; 296662306a36Sopenharmony_ci cmd->data = compat_ptr(v32.data); 296762306a36Sopenharmony_ci cmd->data_len = v32.data_len; 296862306a36Sopenharmony_ci return 0; 296962306a36Sopenharmony_ci} 297062306a36Sopenharmony_ci 297162306a36Sopenharmony_ci/* Copy native cmd structure to 32-bit cmd structure. */ 297262306a36Sopenharmony_cistatic int put_compat_cmd(struct comedi32_cmd_struct __user *cmd32, 297362306a36Sopenharmony_ci struct comedi_cmd *cmd) 297462306a36Sopenharmony_ci{ 297562306a36Sopenharmony_ci struct comedi32_cmd_struct v32; 297662306a36Sopenharmony_ci 297762306a36Sopenharmony_ci memset(&v32, 0, sizeof(v32)); 297862306a36Sopenharmony_ci v32.subdev = cmd->subdev; 297962306a36Sopenharmony_ci v32.flags = cmd->flags; 298062306a36Sopenharmony_ci v32.start_src = cmd->start_src; 298162306a36Sopenharmony_ci v32.start_arg = cmd->start_arg; 298262306a36Sopenharmony_ci v32.scan_begin_src = cmd->scan_begin_src; 298362306a36Sopenharmony_ci v32.scan_begin_arg = cmd->scan_begin_arg; 298462306a36Sopenharmony_ci v32.convert_src = cmd->convert_src; 298562306a36Sopenharmony_ci v32.convert_arg = cmd->convert_arg; 298662306a36Sopenharmony_ci v32.scan_end_src = cmd->scan_end_src; 298762306a36Sopenharmony_ci v32.scan_end_arg = cmd->scan_end_arg; 298862306a36Sopenharmony_ci v32.stop_src = cmd->stop_src; 298962306a36Sopenharmony_ci v32.stop_arg = cmd->stop_arg; 299062306a36Sopenharmony_ci /* Assume chanlist pointer is unchanged. */ 299162306a36Sopenharmony_ci v32.chanlist = ptr_to_compat((unsigned int __user *)cmd->chanlist); 299262306a36Sopenharmony_ci v32.chanlist_len = cmd->chanlist_len; 299362306a36Sopenharmony_ci v32.data = ptr_to_compat(cmd->data); 299462306a36Sopenharmony_ci v32.data_len = cmd->data_len; 299562306a36Sopenharmony_ci if (copy_to_user(cmd32, &v32, sizeof(v32))) 299662306a36Sopenharmony_ci return -EFAULT; 299762306a36Sopenharmony_ci return 0; 299862306a36Sopenharmony_ci} 299962306a36Sopenharmony_ci 300062306a36Sopenharmony_ci/* Handle 32-bit COMEDI_CMD ioctl. */ 300162306a36Sopenharmony_cistatic int compat_cmd(struct file *file, unsigned long arg) 300262306a36Sopenharmony_ci{ 300362306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 300462306a36Sopenharmony_ci struct comedi_device *dev = cfp->dev; 300562306a36Sopenharmony_ci struct comedi_cmd cmd; 300662306a36Sopenharmony_ci bool copy = false; 300762306a36Sopenharmony_ci int rc, err; 300862306a36Sopenharmony_ci 300962306a36Sopenharmony_ci rc = get_compat_cmd(&cmd, compat_ptr(arg)); 301062306a36Sopenharmony_ci if (rc) 301162306a36Sopenharmony_ci return rc; 301262306a36Sopenharmony_ci 301362306a36Sopenharmony_ci mutex_lock(&dev->mutex); 301462306a36Sopenharmony_ci rc = do_cmd_ioctl(dev, &cmd, ©, file); 301562306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 301662306a36Sopenharmony_ci if (copy) { 301762306a36Sopenharmony_ci /* Special case: copy cmd back to user. */ 301862306a36Sopenharmony_ci err = put_compat_cmd(compat_ptr(arg), &cmd); 301962306a36Sopenharmony_ci if (err) 302062306a36Sopenharmony_ci rc = err; 302162306a36Sopenharmony_ci } 302262306a36Sopenharmony_ci return rc; 302362306a36Sopenharmony_ci} 302462306a36Sopenharmony_ci 302562306a36Sopenharmony_ci/* Handle 32-bit COMEDI_CMDTEST ioctl. */ 302662306a36Sopenharmony_cistatic int compat_cmdtest(struct file *file, unsigned long arg) 302762306a36Sopenharmony_ci{ 302862306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 302962306a36Sopenharmony_ci struct comedi_device *dev = cfp->dev; 303062306a36Sopenharmony_ci struct comedi_cmd cmd; 303162306a36Sopenharmony_ci bool copy = false; 303262306a36Sopenharmony_ci int rc, err; 303362306a36Sopenharmony_ci 303462306a36Sopenharmony_ci rc = get_compat_cmd(&cmd, compat_ptr(arg)); 303562306a36Sopenharmony_ci if (rc) 303662306a36Sopenharmony_ci return rc; 303762306a36Sopenharmony_ci 303862306a36Sopenharmony_ci mutex_lock(&dev->mutex); 303962306a36Sopenharmony_ci rc = do_cmdtest_ioctl(dev, &cmd, ©, file); 304062306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 304162306a36Sopenharmony_ci if (copy) { 304262306a36Sopenharmony_ci err = put_compat_cmd(compat_ptr(arg), &cmd); 304362306a36Sopenharmony_ci if (err) 304462306a36Sopenharmony_ci rc = err; 304562306a36Sopenharmony_ci } 304662306a36Sopenharmony_ci return rc; 304762306a36Sopenharmony_ci} 304862306a36Sopenharmony_ci 304962306a36Sopenharmony_ci/* Copy 32-bit insn structure to native insn structure. */ 305062306a36Sopenharmony_cistatic int get_compat_insn(struct comedi_insn *insn, 305162306a36Sopenharmony_ci struct comedi32_insn_struct __user *insn32) 305262306a36Sopenharmony_ci{ 305362306a36Sopenharmony_ci struct comedi32_insn_struct v32; 305462306a36Sopenharmony_ci 305562306a36Sopenharmony_ci /* Copy insn structure. Ignore the unused members. */ 305662306a36Sopenharmony_ci if (copy_from_user(&v32, insn32, sizeof(v32))) 305762306a36Sopenharmony_ci return -EFAULT; 305862306a36Sopenharmony_ci memset(insn, 0, sizeof(*insn)); 305962306a36Sopenharmony_ci insn->insn = v32.insn; 306062306a36Sopenharmony_ci insn->n = v32.n; 306162306a36Sopenharmony_ci insn->data = compat_ptr(v32.data); 306262306a36Sopenharmony_ci insn->subdev = v32.subdev; 306362306a36Sopenharmony_ci insn->chanspec = v32.chanspec; 306462306a36Sopenharmony_ci return 0; 306562306a36Sopenharmony_ci} 306662306a36Sopenharmony_ci 306762306a36Sopenharmony_ci/* Handle 32-bit COMEDI_INSNLIST ioctl. */ 306862306a36Sopenharmony_cistatic int compat_insnlist(struct file *file, unsigned long arg) 306962306a36Sopenharmony_ci{ 307062306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 307162306a36Sopenharmony_ci struct comedi_device *dev = cfp->dev; 307262306a36Sopenharmony_ci struct comedi32_insnlist_struct insnlist32; 307362306a36Sopenharmony_ci struct comedi32_insn_struct __user *insn32; 307462306a36Sopenharmony_ci struct comedi_insn *insns; 307562306a36Sopenharmony_ci unsigned int n; 307662306a36Sopenharmony_ci int rc; 307762306a36Sopenharmony_ci 307862306a36Sopenharmony_ci if (copy_from_user(&insnlist32, compat_ptr(arg), sizeof(insnlist32))) 307962306a36Sopenharmony_ci return -EFAULT; 308062306a36Sopenharmony_ci 308162306a36Sopenharmony_ci insns = kcalloc(insnlist32.n_insns, sizeof(*insns), GFP_KERNEL); 308262306a36Sopenharmony_ci if (!insns) 308362306a36Sopenharmony_ci return -ENOMEM; 308462306a36Sopenharmony_ci 308562306a36Sopenharmony_ci /* Copy insn structures. */ 308662306a36Sopenharmony_ci insn32 = compat_ptr(insnlist32.insns); 308762306a36Sopenharmony_ci for (n = 0; n < insnlist32.n_insns; n++) { 308862306a36Sopenharmony_ci rc = get_compat_insn(insns + n, insn32 + n); 308962306a36Sopenharmony_ci if (rc) { 309062306a36Sopenharmony_ci kfree(insns); 309162306a36Sopenharmony_ci return rc; 309262306a36Sopenharmony_ci } 309362306a36Sopenharmony_ci } 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_ci mutex_lock(&dev->mutex); 309662306a36Sopenharmony_ci rc = do_insnlist_ioctl(dev, insns, insnlist32.n_insns, file); 309762306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 309862306a36Sopenharmony_ci kfree(insns); 309962306a36Sopenharmony_ci return rc; 310062306a36Sopenharmony_ci} 310162306a36Sopenharmony_ci 310262306a36Sopenharmony_ci/* Handle 32-bit COMEDI_INSN ioctl. */ 310362306a36Sopenharmony_cistatic int compat_insn(struct file *file, unsigned long arg) 310462306a36Sopenharmony_ci{ 310562306a36Sopenharmony_ci struct comedi_file *cfp = file->private_data; 310662306a36Sopenharmony_ci struct comedi_device *dev = cfp->dev; 310762306a36Sopenharmony_ci struct comedi_insn insn; 310862306a36Sopenharmony_ci int rc; 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_ci rc = get_compat_insn(&insn, (void __user *)arg); 311162306a36Sopenharmony_ci if (rc) 311262306a36Sopenharmony_ci return rc; 311362306a36Sopenharmony_ci 311462306a36Sopenharmony_ci mutex_lock(&dev->mutex); 311562306a36Sopenharmony_ci rc = do_insn_ioctl(dev, &insn, file); 311662306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 311762306a36Sopenharmony_ci return rc; 311862306a36Sopenharmony_ci} 311962306a36Sopenharmony_ci 312062306a36Sopenharmony_ci/* 312162306a36Sopenharmony_ci * compat_ioctl file operation. 312262306a36Sopenharmony_ci * 312362306a36Sopenharmony_ci * Returns -ENOIOCTLCMD for unrecognised ioctl codes. 312462306a36Sopenharmony_ci */ 312562306a36Sopenharmony_cistatic long comedi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 312662306a36Sopenharmony_ci{ 312762306a36Sopenharmony_ci int rc; 312862306a36Sopenharmony_ci 312962306a36Sopenharmony_ci switch (cmd) { 313062306a36Sopenharmony_ci case COMEDI_DEVCONFIG: 313162306a36Sopenharmony_ci case COMEDI_DEVINFO: 313262306a36Sopenharmony_ci case COMEDI_SUBDINFO: 313362306a36Sopenharmony_ci case COMEDI_BUFCONFIG: 313462306a36Sopenharmony_ci case COMEDI_BUFINFO: 313562306a36Sopenharmony_ci /* Just need to translate the pointer argument. */ 313662306a36Sopenharmony_ci arg = (unsigned long)compat_ptr(arg); 313762306a36Sopenharmony_ci rc = comedi_unlocked_ioctl(file, cmd, arg); 313862306a36Sopenharmony_ci break; 313962306a36Sopenharmony_ci case COMEDI_LOCK: 314062306a36Sopenharmony_ci case COMEDI_UNLOCK: 314162306a36Sopenharmony_ci case COMEDI_CANCEL: 314262306a36Sopenharmony_ci case COMEDI_POLL: 314362306a36Sopenharmony_ci case COMEDI_SETRSUBD: 314462306a36Sopenharmony_ci case COMEDI_SETWSUBD: 314562306a36Sopenharmony_ci /* No translation needed. */ 314662306a36Sopenharmony_ci rc = comedi_unlocked_ioctl(file, cmd, arg); 314762306a36Sopenharmony_ci break; 314862306a36Sopenharmony_ci case COMEDI32_CHANINFO: 314962306a36Sopenharmony_ci rc = compat_chaninfo(file, arg); 315062306a36Sopenharmony_ci break; 315162306a36Sopenharmony_ci case COMEDI32_RANGEINFO: 315262306a36Sopenharmony_ci rc = compat_rangeinfo(file, arg); 315362306a36Sopenharmony_ci break; 315462306a36Sopenharmony_ci case COMEDI32_CMD: 315562306a36Sopenharmony_ci rc = compat_cmd(file, arg); 315662306a36Sopenharmony_ci break; 315762306a36Sopenharmony_ci case COMEDI32_CMDTEST: 315862306a36Sopenharmony_ci rc = compat_cmdtest(file, arg); 315962306a36Sopenharmony_ci break; 316062306a36Sopenharmony_ci case COMEDI32_INSNLIST: 316162306a36Sopenharmony_ci rc = compat_insnlist(file, arg); 316262306a36Sopenharmony_ci break; 316362306a36Sopenharmony_ci case COMEDI32_INSN: 316462306a36Sopenharmony_ci rc = compat_insn(file, arg); 316562306a36Sopenharmony_ci break; 316662306a36Sopenharmony_ci default: 316762306a36Sopenharmony_ci rc = -ENOIOCTLCMD; 316862306a36Sopenharmony_ci break; 316962306a36Sopenharmony_ci } 317062306a36Sopenharmony_ci return rc; 317162306a36Sopenharmony_ci} 317262306a36Sopenharmony_ci#else 317362306a36Sopenharmony_ci#define comedi_compat_ioctl NULL 317462306a36Sopenharmony_ci#endif 317562306a36Sopenharmony_ci 317662306a36Sopenharmony_cistatic const struct file_operations comedi_fops = { 317762306a36Sopenharmony_ci .owner = THIS_MODULE, 317862306a36Sopenharmony_ci .unlocked_ioctl = comedi_unlocked_ioctl, 317962306a36Sopenharmony_ci .compat_ioctl = comedi_compat_ioctl, 318062306a36Sopenharmony_ci .open = comedi_open, 318162306a36Sopenharmony_ci .release = comedi_close, 318262306a36Sopenharmony_ci .read = comedi_read, 318362306a36Sopenharmony_ci .write = comedi_write, 318462306a36Sopenharmony_ci .mmap = comedi_mmap, 318562306a36Sopenharmony_ci .poll = comedi_poll, 318662306a36Sopenharmony_ci .fasync = comedi_fasync, 318762306a36Sopenharmony_ci .llseek = noop_llseek, 318862306a36Sopenharmony_ci}; 318962306a36Sopenharmony_ci 319062306a36Sopenharmony_ci/** 319162306a36Sopenharmony_ci * comedi_event() - Handle events for asynchronous COMEDI command 319262306a36Sopenharmony_ci * @dev: COMEDI device. 319362306a36Sopenharmony_ci * @s: COMEDI subdevice. 319462306a36Sopenharmony_ci * Context: in_interrupt() (usually), @s->spin_lock spin-lock not held. 319562306a36Sopenharmony_ci * 319662306a36Sopenharmony_ci * If an asynchronous COMEDI command is active on the subdevice, process 319762306a36Sopenharmony_ci * any %COMEDI_CB_... event flags that have been set, usually by an 319862306a36Sopenharmony_ci * interrupt handler. These may change the run state of the asynchronous 319962306a36Sopenharmony_ci * command, wake a task, and/or send a %SIGIO signal. 320062306a36Sopenharmony_ci */ 320162306a36Sopenharmony_civoid comedi_event(struct comedi_device *dev, struct comedi_subdevice *s) 320262306a36Sopenharmony_ci{ 320362306a36Sopenharmony_ci struct comedi_async *async = s->async; 320462306a36Sopenharmony_ci unsigned int events; 320562306a36Sopenharmony_ci int si_code = 0; 320662306a36Sopenharmony_ci unsigned long flags; 320762306a36Sopenharmony_ci 320862306a36Sopenharmony_ci spin_lock_irqsave(&s->spin_lock, flags); 320962306a36Sopenharmony_ci 321062306a36Sopenharmony_ci events = async->events; 321162306a36Sopenharmony_ci async->events = 0; 321262306a36Sopenharmony_ci if (!__comedi_is_subdevice_running(s)) { 321362306a36Sopenharmony_ci spin_unlock_irqrestore(&s->spin_lock, flags); 321462306a36Sopenharmony_ci return; 321562306a36Sopenharmony_ci } 321662306a36Sopenharmony_ci 321762306a36Sopenharmony_ci if (events & COMEDI_CB_CANCEL_MASK) 321862306a36Sopenharmony_ci __comedi_clear_subdevice_runflags(s, COMEDI_SRF_RUNNING); 321962306a36Sopenharmony_ci 322062306a36Sopenharmony_ci /* 322162306a36Sopenharmony_ci * Remember if an error event has occurred, so an error can be 322262306a36Sopenharmony_ci * returned the next time the user does a read() or write(). 322362306a36Sopenharmony_ci */ 322462306a36Sopenharmony_ci if (events & COMEDI_CB_ERROR_MASK) 322562306a36Sopenharmony_ci __comedi_set_subdevice_runflags(s, COMEDI_SRF_ERROR); 322662306a36Sopenharmony_ci 322762306a36Sopenharmony_ci if (async->cb_mask & events) { 322862306a36Sopenharmony_ci wake_up_interruptible(&async->wait_head); 322962306a36Sopenharmony_ci si_code = async->cmd.flags & CMDF_WRITE ? POLL_OUT : POLL_IN; 323062306a36Sopenharmony_ci } 323162306a36Sopenharmony_ci 323262306a36Sopenharmony_ci spin_unlock_irqrestore(&s->spin_lock, flags); 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_ci if (si_code) 323562306a36Sopenharmony_ci kill_fasync(&dev->async_queue, SIGIO, si_code); 323662306a36Sopenharmony_ci} 323762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(comedi_event); 323862306a36Sopenharmony_ci 323962306a36Sopenharmony_ci/* Note: the ->mutex is pre-locked on successful return */ 324062306a36Sopenharmony_cistruct comedi_device *comedi_alloc_board_minor(struct device *hardware_device) 324162306a36Sopenharmony_ci{ 324262306a36Sopenharmony_ci struct comedi_device *dev; 324362306a36Sopenharmony_ci struct device *csdev; 324462306a36Sopenharmony_ci unsigned int i; 324562306a36Sopenharmony_ci 324662306a36Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 324762306a36Sopenharmony_ci if (!dev) 324862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 324962306a36Sopenharmony_ci comedi_device_init(dev); 325062306a36Sopenharmony_ci comedi_set_hw_dev(dev, hardware_device); 325162306a36Sopenharmony_ci mutex_lock(&dev->mutex); 325262306a36Sopenharmony_ci mutex_lock(&comedi_board_minor_table_lock); 325362306a36Sopenharmony_ci for (i = hardware_device ? comedi_num_legacy_minors : 0; 325462306a36Sopenharmony_ci i < COMEDI_NUM_BOARD_MINORS; ++i) { 325562306a36Sopenharmony_ci if (!comedi_board_minor_table[i]) { 325662306a36Sopenharmony_ci comedi_board_minor_table[i] = dev; 325762306a36Sopenharmony_ci break; 325862306a36Sopenharmony_ci } 325962306a36Sopenharmony_ci } 326062306a36Sopenharmony_ci mutex_unlock(&comedi_board_minor_table_lock); 326162306a36Sopenharmony_ci if (i == COMEDI_NUM_BOARD_MINORS) { 326262306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 326362306a36Sopenharmony_ci comedi_device_cleanup(dev); 326462306a36Sopenharmony_ci comedi_dev_put(dev); 326562306a36Sopenharmony_ci dev_err(hardware_device, 326662306a36Sopenharmony_ci "ran out of minor numbers for board device files\n"); 326762306a36Sopenharmony_ci return ERR_PTR(-EBUSY); 326862306a36Sopenharmony_ci } 326962306a36Sopenharmony_ci dev->minor = i; 327062306a36Sopenharmony_ci csdev = device_create(&comedi_class, hardware_device, 327162306a36Sopenharmony_ci MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i", i); 327262306a36Sopenharmony_ci if (!IS_ERR(csdev)) 327362306a36Sopenharmony_ci dev->class_dev = get_device(csdev); 327462306a36Sopenharmony_ci 327562306a36Sopenharmony_ci /* Note: dev->mutex needs to be unlocked by the caller. */ 327662306a36Sopenharmony_ci return dev; 327762306a36Sopenharmony_ci} 327862306a36Sopenharmony_ci 327962306a36Sopenharmony_civoid comedi_release_hardware_device(struct device *hardware_device) 328062306a36Sopenharmony_ci{ 328162306a36Sopenharmony_ci int minor; 328262306a36Sopenharmony_ci struct comedi_device *dev; 328362306a36Sopenharmony_ci 328462306a36Sopenharmony_ci for (minor = comedi_num_legacy_minors; minor < COMEDI_NUM_BOARD_MINORS; 328562306a36Sopenharmony_ci minor++) { 328662306a36Sopenharmony_ci mutex_lock(&comedi_board_minor_table_lock); 328762306a36Sopenharmony_ci dev = comedi_board_minor_table[minor]; 328862306a36Sopenharmony_ci if (dev && dev->hw_dev == hardware_device) { 328962306a36Sopenharmony_ci comedi_board_minor_table[minor] = NULL; 329062306a36Sopenharmony_ci mutex_unlock(&comedi_board_minor_table_lock); 329162306a36Sopenharmony_ci comedi_free_board_dev(dev); 329262306a36Sopenharmony_ci break; 329362306a36Sopenharmony_ci } 329462306a36Sopenharmony_ci mutex_unlock(&comedi_board_minor_table_lock); 329562306a36Sopenharmony_ci } 329662306a36Sopenharmony_ci} 329762306a36Sopenharmony_ci 329862306a36Sopenharmony_ciint comedi_alloc_subdevice_minor(struct comedi_subdevice *s) 329962306a36Sopenharmony_ci{ 330062306a36Sopenharmony_ci struct comedi_device *dev = s->device; 330162306a36Sopenharmony_ci struct device *csdev; 330262306a36Sopenharmony_ci unsigned int i; 330362306a36Sopenharmony_ci 330462306a36Sopenharmony_ci mutex_lock(&comedi_subdevice_minor_table_lock); 330562306a36Sopenharmony_ci for (i = 0; i < COMEDI_NUM_SUBDEVICE_MINORS; ++i) { 330662306a36Sopenharmony_ci if (!comedi_subdevice_minor_table[i]) { 330762306a36Sopenharmony_ci comedi_subdevice_minor_table[i] = s; 330862306a36Sopenharmony_ci break; 330962306a36Sopenharmony_ci } 331062306a36Sopenharmony_ci } 331162306a36Sopenharmony_ci mutex_unlock(&comedi_subdevice_minor_table_lock); 331262306a36Sopenharmony_ci if (i == COMEDI_NUM_SUBDEVICE_MINORS) { 331362306a36Sopenharmony_ci dev_err(dev->class_dev, 331462306a36Sopenharmony_ci "ran out of minor numbers for subdevice files\n"); 331562306a36Sopenharmony_ci return -EBUSY; 331662306a36Sopenharmony_ci } 331762306a36Sopenharmony_ci i += COMEDI_NUM_BOARD_MINORS; 331862306a36Sopenharmony_ci s->minor = i; 331962306a36Sopenharmony_ci csdev = device_create(&comedi_class, dev->class_dev, 332062306a36Sopenharmony_ci MKDEV(COMEDI_MAJOR, i), NULL, "comedi%i_subd%i", 332162306a36Sopenharmony_ci dev->minor, s->index); 332262306a36Sopenharmony_ci if (!IS_ERR(csdev)) 332362306a36Sopenharmony_ci s->class_dev = csdev; 332462306a36Sopenharmony_ci 332562306a36Sopenharmony_ci return 0; 332662306a36Sopenharmony_ci} 332762306a36Sopenharmony_ci 332862306a36Sopenharmony_civoid comedi_free_subdevice_minor(struct comedi_subdevice *s) 332962306a36Sopenharmony_ci{ 333062306a36Sopenharmony_ci unsigned int i; 333162306a36Sopenharmony_ci 333262306a36Sopenharmony_ci if (!s) 333362306a36Sopenharmony_ci return; 333462306a36Sopenharmony_ci if (s->minor < COMEDI_NUM_BOARD_MINORS || 333562306a36Sopenharmony_ci s->minor >= COMEDI_NUM_MINORS) 333662306a36Sopenharmony_ci return; 333762306a36Sopenharmony_ci 333862306a36Sopenharmony_ci i = s->minor - COMEDI_NUM_BOARD_MINORS; 333962306a36Sopenharmony_ci mutex_lock(&comedi_subdevice_minor_table_lock); 334062306a36Sopenharmony_ci if (s == comedi_subdevice_minor_table[i]) 334162306a36Sopenharmony_ci comedi_subdevice_minor_table[i] = NULL; 334262306a36Sopenharmony_ci mutex_unlock(&comedi_subdevice_minor_table_lock); 334362306a36Sopenharmony_ci if (s->class_dev) { 334462306a36Sopenharmony_ci device_destroy(&comedi_class, MKDEV(COMEDI_MAJOR, s->minor)); 334562306a36Sopenharmony_ci s->class_dev = NULL; 334662306a36Sopenharmony_ci } 334762306a36Sopenharmony_ci} 334862306a36Sopenharmony_ci 334962306a36Sopenharmony_cistatic void comedi_cleanup_board_minors(void) 335062306a36Sopenharmony_ci{ 335162306a36Sopenharmony_ci struct comedi_device *dev; 335262306a36Sopenharmony_ci unsigned int i; 335362306a36Sopenharmony_ci 335462306a36Sopenharmony_ci for (i = 0; i < COMEDI_NUM_BOARD_MINORS; i++) { 335562306a36Sopenharmony_ci dev = comedi_clear_board_minor(i); 335662306a36Sopenharmony_ci comedi_free_board_dev(dev); 335762306a36Sopenharmony_ci } 335862306a36Sopenharmony_ci} 335962306a36Sopenharmony_ci 336062306a36Sopenharmony_cistatic int __init comedi_init(void) 336162306a36Sopenharmony_ci{ 336262306a36Sopenharmony_ci int i; 336362306a36Sopenharmony_ci int retval; 336462306a36Sopenharmony_ci 336562306a36Sopenharmony_ci pr_info("version " COMEDI_RELEASE " - http://www.comedi.org\n"); 336662306a36Sopenharmony_ci 336762306a36Sopenharmony_ci if (comedi_num_legacy_minors > COMEDI_NUM_BOARD_MINORS) { 336862306a36Sopenharmony_ci pr_err("invalid value for module parameter \"comedi_num_legacy_minors\". Valid values are 0 through %i.\n", 336962306a36Sopenharmony_ci COMEDI_NUM_BOARD_MINORS); 337062306a36Sopenharmony_ci return -EINVAL; 337162306a36Sopenharmony_ci } 337262306a36Sopenharmony_ci 337362306a36Sopenharmony_ci retval = register_chrdev_region(MKDEV(COMEDI_MAJOR, 0), 337462306a36Sopenharmony_ci COMEDI_NUM_MINORS, "comedi"); 337562306a36Sopenharmony_ci if (retval) 337662306a36Sopenharmony_ci return retval; 337762306a36Sopenharmony_ci 337862306a36Sopenharmony_ci cdev_init(&comedi_cdev, &comedi_fops); 337962306a36Sopenharmony_ci comedi_cdev.owner = THIS_MODULE; 338062306a36Sopenharmony_ci 338162306a36Sopenharmony_ci retval = kobject_set_name(&comedi_cdev.kobj, "comedi"); 338262306a36Sopenharmony_ci if (retval) 338362306a36Sopenharmony_ci goto out_unregister_chrdev_region; 338462306a36Sopenharmony_ci 338562306a36Sopenharmony_ci retval = cdev_add(&comedi_cdev, MKDEV(COMEDI_MAJOR, 0), 338662306a36Sopenharmony_ci COMEDI_NUM_MINORS); 338762306a36Sopenharmony_ci if (retval) 338862306a36Sopenharmony_ci goto out_unregister_chrdev_region; 338962306a36Sopenharmony_ci 339062306a36Sopenharmony_ci retval = class_register(&comedi_class); 339162306a36Sopenharmony_ci if (retval) { 339262306a36Sopenharmony_ci pr_err("failed to create class\n"); 339362306a36Sopenharmony_ci goto out_cdev_del; 339462306a36Sopenharmony_ci } 339562306a36Sopenharmony_ci 339662306a36Sopenharmony_ci /* create devices files for legacy/manual use */ 339762306a36Sopenharmony_ci for (i = 0; i < comedi_num_legacy_minors; i++) { 339862306a36Sopenharmony_ci struct comedi_device *dev; 339962306a36Sopenharmony_ci 340062306a36Sopenharmony_ci dev = comedi_alloc_board_minor(NULL); 340162306a36Sopenharmony_ci if (IS_ERR(dev)) { 340262306a36Sopenharmony_ci retval = PTR_ERR(dev); 340362306a36Sopenharmony_ci goto out_cleanup_board_minors; 340462306a36Sopenharmony_ci } 340562306a36Sopenharmony_ci /* comedi_alloc_board_minor() locked the mutex */ 340662306a36Sopenharmony_ci lockdep_assert_held(&dev->mutex); 340762306a36Sopenharmony_ci mutex_unlock(&dev->mutex); 340862306a36Sopenharmony_ci } 340962306a36Sopenharmony_ci 341062306a36Sopenharmony_ci /* XXX requires /proc interface */ 341162306a36Sopenharmony_ci comedi_proc_init(); 341262306a36Sopenharmony_ci 341362306a36Sopenharmony_ci return 0; 341462306a36Sopenharmony_ci 341562306a36Sopenharmony_ciout_cleanup_board_minors: 341662306a36Sopenharmony_ci comedi_cleanup_board_minors(); 341762306a36Sopenharmony_ci class_unregister(&comedi_class); 341862306a36Sopenharmony_ciout_cdev_del: 341962306a36Sopenharmony_ci cdev_del(&comedi_cdev); 342062306a36Sopenharmony_ciout_unregister_chrdev_region: 342162306a36Sopenharmony_ci unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS); 342262306a36Sopenharmony_ci return retval; 342362306a36Sopenharmony_ci} 342462306a36Sopenharmony_cimodule_init(comedi_init); 342562306a36Sopenharmony_ci 342662306a36Sopenharmony_cistatic void __exit comedi_cleanup(void) 342762306a36Sopenharmony_ci{ 342862306a36Sopenharmony_ci comedi_cleanup_board_minors(); 342962306a36Sopenharmony_ci class_unregister(&comedi_class); 343062306a36Sopenharmony_ci cdev_del(&comedi_cdev); 343162306a36Sopenharmony_ci unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0), COMEDI_NUM_MINORS); 343262306a36Sopenharmony_ci 343362306a36Sopenharmony_ci comedi_proc_cleanup(); 343462306a36Sopenharmony_ci} 343562306a36Sopenharmony_cimodule_exit(comedi_cleanup); 343662306a36Sopenharmony_ci 343762306a36Sopenharmony_ciMODULE_AUTHOR("https://www.comedi.org"); 343862306a36Sopenharmony_ciMODULE_DESCRIPTION("Comedi core module"); 343962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3440