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, &copy, 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, &copy, 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, &copy, 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, &copy, 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