18ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+ 28ffdff6aSGreg Kroah-Hartman /* 38ffdff6aSGreg Kroah-Hartman * module/drivers.c 48ffdff6aSGreg Kroah-Hartman * functions for manipulating drivers 58ffdff6aSGreg Kroah-Hartman * 68ffdff6aSGreg Kroah-Hartman * COMEDI - Linux Control and Measurement Device Interface 78ffdff6aSGreg Kroah-Hartman * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org> 88ffdff6aSGreg Kroah-Hartman * Copyright (C) 2002 Frank Mori Hess <fmhess@users.sourceforge.net> 98ffdff6aSGreg Kroah-Hartman */ 108ffdff6aSGreg Kroah-Hartman 118ffdff6aSGreg Kroah-Hartman #include <linux/device.h> 128ffdff6aSGreg Kroah-Hartman #include <linux/module.h> 138ffdff6aSGreg Kroah-Hartman #include <linux/errno.h> 148ffdff6aSGreg Kroah-Hartman #include <linux/kernel.h> 158ffdff6aSGreg Kroah-Hartman #include <linux/ioport.h> 168ffdff6aSGreg Kroah-Hartman #include <linux/slab.h> 178ffdff6aSGreg Kroah-Hartman #include <linux/dma-direction.h> 188ffdff6aSGreg Kroah-Hartman #include <linux/interrupt.h> 198ffdff6aSGreg Kroah-Hartman #include <linux/firmware.h> 20*df0e68c1SIan Abbott #include <linux/comedi/comedidev.h> 218ffdff6aSGreg Kroah-Hartman #include "comedi_internal.h" 228ffdff6aSGreg Kroah-Hartman 238ffdff6aSGreg Kroah-Hartman struct comedi_driver *comedi_drivers; 248ffdff6aSGreg Kroah-Hartman /* protects access to comedi_drivers */ 258ffdff6aSGreg Kroah-Hartman DEFINE_MUTEX(comedi_drivers_list_lock); 268ffdff6aSGreg Kroah-Hartman 278ffdff6aSGreg Kroah-Hartman /** 288ffdff6aSGreg Kroah-Hartman * comedi_set_hw_dev() - Set hardware device associated with COMEDI device 298ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 308ffdff6aSGreg Kroah-Hartman * @hw_dev: Hardware device. 318ffdff6aSGreg Kroah-Hartman * 328ffdff6aSGreg Kroah-Hartman * For automatically configured COMEDI devices (resulting from a call to 338ffdff6aSGreg Kroah-Hartman * comedi_auto_config() or one of its wrappers from the low-level COMEDI 348ffdff6aSGreg Kroah-Hartman * driver), comedi_set_hw_dev() is called automatically by the COMEDI core 358ffdff6aSGreg Kroah-Hartman * to associate the COMEDI device with the hardware device. It can also be 368ffdff6aSGreg Kroah-Hartman * called directly by "legacy" low-level COMEDI drivers that rely on the 378ffdff6aSGreg Kroah-Hartman * %COMEDI_DEVCONFIG ioctl to configure the hardware as long as the hardware 388ffdff6aSGreg Kroah-Hartman * has a &struct device. 398ffdff6aSGreg Kroah-Hartman * 408ffdff6aSGreg Kroah-Hartman * If @dev->hw_dev is NULL, it gets a reference to @hw_dev and sets 418ffdff6aSGreg Kroah-Hartman * @dev->hw_dev, otherwise, it does nothing. Calling it multiple times 428ffdff6aSGreg Kroah-Hartman * with the same hardware device is not considered an error. If it gets 438ffdff6aSGreg Kroah-Hartman * a reference to the hardware device, it will be automatically 'put' when 448ffdff6aSGreg Kroah-Hartman * the device is detached from COMEDI. 458ffdff6aSGreg Kroah-Hartman * 468ffdff6aSGreg Kroah-Hartman * Returns 0 if @dev->hw_dev was NULL or the same as @hw_dev, otherwise 478ffdff6aSGreg Kroah-Hartman * returns -EEXIST. 488ffdff6aSGreg Kroah-Hartman */ 498ffdff6aSGreg Kroah-Hartman int comedi_set_hw_dev(struct comedi_device *dev, struct device *hw_dev) 508ffdff6aSGreg Kroah-Hartman { 518ffdff6aSGreg Kroah-Hartman if (hw_dev == dev->hw_dev) 528ffdff6aSGreg Kroah-Hartman return 0; 538ffdff6aSGreg Kroah-Hartman if (dev->hw_dev) 548ffdff6aSGreg Kroah-Hartman return -EEXIST; 558ffdff6aSGreg Kroah-Hartman dev->hw_dev = get_device(hw_dev); 568ffdff6aSGreg Kroah-Hartman return 0; 578ffdff6aSGreg Kroah-Hartman } 588ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_set_hw_dev); 598ffdff6aSGreg Kroah-Hartman 608ffdff6aSGreg Kroah-Hartman static void comedi_clear_hw_dev(struct comedi_device *dev) 618ffdff6aSGreg Kroah-Hartman { 628ffdff6aSGreg Kroah-Hartman put_device(dev->hw_dev); 638ffdff6aSGreg Kroah-Hartman dev->hw_dev = NULL; 648ffdff6aSGreg Kroah-Hartman } 658ffdff6aSGreg Kroah-Hartman 668ffdff6aSGreg Kroah-Hartman /** 678ffdff6aSGreg Kroah-Hartman * comedi_alloc_devpriv() - Allocate memory for the device private data 688ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 698ffdff6aSGreg Kroah-Hartman * @size: Size of the memory to allocate. 708ffdff6aSGreg Kroah-Hartman * 718ffdff6aSGreg Kroah-Hartman * The allocated memory is zero-filled. @dev->private points to it on 728ffdff6aSGreg Kroah-Hartman * return. The memory will be automatically freed when the COMEDI device is 738ffdff6aSGreg Kroah-Hartman * "detached". 748ffdff6aSGreg Kroah-Hartman * 758ffdff6aSGreg Kroah-Hartman * Returns a pointer to the allocated memory, or NULL on failure. 768ffdff6aSGreg Kroah-Hartman */ 778ffdff6aSGreg Kroah-Hartman void *comedi_alloc_devpriv(struct comedi_device *dev, size_t size) 788ffdff6aSGreg Kroah-Hartman { 798ffdff6aSGreg Kroah-Hartman dev->private = kzalloc(size, GFP_KERNEL); 808ffdff6aSGreg Kroah-Hartman return dev->private; 818ffdff6aSGreg Kroah-Hartman } 828ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_alloc_devpriv); 838ffdff6aSGreg Kroah-Hartman 848ffdff6aSGreg Kroah-Hartman /** 858ffdff6aSGreg Kroah-Hartman * comedi_alloc_subdevices() - Allocate subdevices for COMEDI device 868ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 878ffdff6aSGreg Kroah-Hartman * @num_subdevices: Number of subdevices to allocate. 888ffdff6aSGreg Kroah-Hartman * 898ffdff6aSGreg Kroah-Hartman * Allocates and initializes an array of &struct comedi_subdevice for the 908ffdff6aSGreg Kroah-Hartman * COMEDI device. If successful, sets @dev->subdevices to point to the 918ffdff6aSGreg Kroah-Hartman * first one and @dev->n_subdevices to the number. 928ffdff6aSGreg Kroah-Hartman * 938ffdff6aSGreg Kroah-Hartman * Returns 0 on success, -EINVAL if @num_subdevices is < 1, or -ENOMEM if 948ffdff6aSGreg Kroah-Hartman * failed to allocate the memory. 958ffdff6aSGreg Kroah-Hartman */ 968ffdff6aSGreg Kroah-Hartman int comedi_alloc_subdevices(struct comedi_device *dev, int num_subdevices) 978ffdff6aSGreg Kroah-Hartman { 988ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s; 998ffdff6aSGreg Kroah-Hartman int i; 1008ffdff6aSGreg Kroah-Hartman 1018ffdff6aSGreg Kroah-Hartman if (num_subdevices < 1) 1028ffdff6aSGreg Kroah-Hartman return -EINVAL; 1038ffdff6aSGreg Kroah-Hartman 1048ffdff6aSGreg Kroah-Hartman s = kcalloc(num_subdevices, sizeof(*s), GFP_KERNEL); 1058ffdff6aSGreg Kroah-Hartman if (!s) 1068ffdff6aSGreg Kroah-Hartman return -ENOMEM; 1078ffdff6aSGreg Kroah-Hartman dev->subdevices = s; 1088ffdff6aSGreg Kroah-Hartman dev->n_subdevices = num_subdevices; 1098ffdff6aSGreg Kroah-Hartman 1108ffdff6aSGreg Kroah-Hartman for (i = 0; i < num_subdevices; ++i) { 1118ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[i]; 1128ffdff6aSGreg Kroah-Hartman s->device = dev; 1138ffdff6aSGreg Kroah-Hartman s->index = i; 1148ffdff6aSGreg Kroah-Hartman s->async_dma_dir = DMA_NONE; 1158ffdff6aSGreg Kroah-Hartman spin_lock_init(&s->spin_lock); 1168ffdff6aSGreg Kroah-Hartman s->minor = -1; 1178ffdff6aSGreg Kroah-Hartman } 1188ffdff6aSGreg Kroah-Hartman return 0; 1198ffdff6aSGreg Kroah-Hartman } 1208ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_alloc_subdevices); 1218ffdff6aSGreg Kroah-Hartman 1228ffdff6aSGreg Kroah-Hartman /** 1238ffdff6aSGreg Kroah-Hartman * comedi_alloc_subdev_readback() - Allocate memory for the subdevice readback 1248ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 1258ffdff6aSGreg Kroah-Hartman * 1268ffdff6aSGreg Kroah-Hartman * This is called by low-level COMEDI drivers to allocate an array to record 1278ffdff6aSGreg Kroah-Hartman * the last values written to a subdevice's analog output channels (at least 1288ffdff6aSGreg Kroah-Hartman * by the %INSN_WRITE instruction), to allow them to be read back by an 1298ffdff6aSGreg Kroah-Hartman * %INSN_READ instruction. It also provides a default handler for the 1308ffdff6aSGreg Kroah-Hartman * %INSN_READ instruction unless one has already been set. 1318ffdff6aSGreg Kroah-Hartman * 1328ffdff6aSGreg Kroah-Hartman * On success, @s->readback points to the first element of the array, which 1338ffdff6aSGreg Kroah-Hartman * is zero-filled. The low-level driver is responsible for updating its 1348ffdff6aSGreg Kroah-Hartman * contents. @s->insn_read will be set to comedi_readback_insn_read() 1358ffdff6aSGreg Kroah-Hartman * unless it is already non-NULL. 1368ffdff6aSGreg Kroah-Hartman * 1378ffdff6aSGreg Kroah-Hartman * Returns 0 on success, -EINVAL if the subdevice has no channels, or 1388ffdff6aSGreg Kroah-Hartman * -ENOMEM on allocation failure. 1398ffdff6aSGreg Kroah-Hartman */ 1408ffdff6aSGreg Kroah-Hartman int comedi_alloc_subdev_readback(struct comedi_subdevice *s) 1418ffdff6aSGreg Kroah-Hartman { 1428ffdff6aSGreg Kroah-Hartman if (!s->n_chan) 1438ffdff6aSGreg Kroah-Hartman return -EINVAL; 1448ffdff6aSGreg Kroah-Hartman 1458ffdff6aSGreg Kroah-Hartman s->readback = kcalloc(s->n_chan, sizeof(*s->readback), GFP_KERNEL); 1468ffdff6aSGreg Kroah-Hartman if (!s->readback) 1478ffdff6aSGreg Kroah-Hartman return -ENOMEM; 1488ffdff6aSGreg Kroah-Hartman 1498ffdff6aSGreg Kroah-Hartman if (!s->insn_read) 1508ffdff6aSGreg Kroah-Hartman s->insn_read = comedi_readback_insn_read; 1518ffdff6aSGreg Kroah-Hartman 1528ffdff6aSGreg Kroah-Hartman return 0; 1538ffdff6aSGreg Kroah-Hartman } 1548ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_alloc_subdev_readback); 1558ffdff6aSGreg Kroah-Hartman 1568ffdff6aSGreg Kroah-Hartman static void comedi_device_detach_cleanup(struct comedi_device *dev) 1578ffdff6aSGreg Kroah-Hartman { 1588ffdff6aSGreg Kroah-Hartman int i; 1598ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s; 1608ffdff6aSGreg Kroah-Hartman 1618ffdff6aSGreg Kroah-Hartman lockdep_assert_held(&dev->attach_lock); 1628ffdff6aSGreg Kroah-Hartman lockdep_assert_held(&dev->mutex); 1638ffdff6aSGreg Kroah-Hartman if (dev->subdevices) { 1648ffdff6aSGreg Kroah-Hartman for (i = 0; i < dev->n_subdevices; i++) { 1658ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[i]; 1668ffdff6aSGreg Kroah-Hartman if (comedi_can_auto_free_spriv(s)) 1678ffdff6aSGreg Kroah-Hartman kfree(s->private); 1688ffdff6aSGreg Kroah-Hartman comedi_free_subdevice_minor(s); 1698ffdff6aSGreg Kroah-Hartman if (s->async) { 1708ffdff6aSGreg Kroah-Hartman comedi_buf_alloc(dev, s, 0); 1718ffdff6aSGreg Kroah-Hartman kfree(s->async); 1728ffdff6aSGreg Kroah-Hartman } 1738ffdff6aSGreg Kroah-Hartman kfree(s->readback); 1748ffdff6aSGreg Kroah-Hartman } 1758ffdff6aSGreg Kroah-Hartman kfree(dev->subdevices); 1768ffdff6aSGreg Kroah-Hartman dev->subdevices = NULL; 1778ffdff6aSGreg Kroah-Hartman dev->n_subdevices = 0; 1788ffdff6aSGreg Kroah-Hartman } 1798ffdff6aSGreg Kroah-Hartman kfree(dev->private); 1808ffdff6aSGreg Kroah-Hartman kfree(dev->pacer); 1818ffdff6aSGreg Kroah-Hartman dev->private = NULL; 1828ffdff6aSGreg Kroah-Hartman dev->pacer = NULL; 1838ffdff6aSGreg Kroah-Hartman dev->driver = NULL; 1848ffdff6aSGreg Kroah-Hartman dev->board_name = NULL; 1858ffdff6aSGreg Kroah-Hartman dev->board_ptr = NULL; 1868ffdff6aSGreg Kroah-Hartman dev->mmio = NULL; 1878ffdff6aSGreg Kroah-Hartman dev->iobase = 0; 1888ffdff6aSGreg Kroah-Hartman dev->iolen = 0; 1898ffdff6aSGreg Kroah-Hartman dev->ioenabled = false; 1908ffdff6aSGreg Kroah-Hartman dev->irq = 0; 1918ffdff6aSGreg Kroah-Hartman dev->read_subdev = NULL; 1928ffdff6aSGreg Kroah-Hartman dev->write_subdev = NULL; 1938ffdff6aSGreg Kroah-Hartman dev->open = NULL; 1948ffdff6aSGreg Kroah-Hartman dev->close = NULL; 1958ffdff6aSGreg Kroah-Hartman comedi_clear_hw_dev(dev); 1968ffdff6aSGreg Kroah-Hartman } 1978ffdff6aSGreg Kroah-Hartman 1988ffdff6aSGreg Kroah-Hartman void comedi_device_detach(struct comedi_device *dev) 1998ffdff6aSGreg Kroah-Hartman { 2008ffdff6aSGreg Kroah-Hartman lockdep_assert_held(&dev->mutex); 2018ffdff6aSGreg Kroah-Hartman comedi_device_cancel_all(dev); 2028ffdff6aSGreg Kroah-Hartman down_write(&dev->attach_lock); 2038ffdff6aSGreg Kroah-Hartman dev->attached = false; 2048ffdff6aSGreg Kroah-Hartman dev->detach_count++; 2058ffdff6aSGreg Kroah-Hartman if (dev->driver) 2068ffdff6aSGreg Kroah-Hartman dev->driver->detach(dev); 2078ffdff6aSGreg Kroah-Hartman comedi_device_detach_cleanup(dev); 2088ffdff6aSGreg Kroah-Hartman up_write(&dev->attach_lock); 2098ffdff6aSGreg Kroah-Hartman } 2108ffdff6aSGreg Kroah-Hartman 2118ffdff6aSGreg Kroah-Hartman static int poll_invalid(struct comedi_device *dev, struct comedi_subdevice *s) 2128ffdff6aSGreg Kroah-Hartman { 2138ffdff6aSGreg Kroah-Hartman return -EINVAL; 2148ffdff6aSGreg Kroah-Hartman } 2158ffdff6aSGreg Kroah-Hartman 2168ffdff6aSGreg Kroah-Hartman static int insn_device_inval(struct comedi_device *dev, 2178ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, unsigned int *data) 2188ffdff6aSGreg Kroah-Hartman { 2198ffdff6aSGreg Kroah-Hartman return -EINVAL; 2208ffdff6aSGreg Kroah-Hartman } 2218ffdff6aSGreg Kroah-Hartman 2228ffdff6aSGreg Kroah-Hartman static unsigned int get_zero_valid_routes(struct comedi_device *dev, 2238ffdff6aSGreg Kroah-Hartman unsigned int n_pairs, 2248ffdff6aSGreg Kroah-Hartman unsigned int *pair_data) 2258ffdff6aSGreg Kroah-Hartman { 2268ffdff6aSGreg Kroah-Hartman return 0; 2278ffdff6aSGreg Kroah-Hartman } 2288ffdff6aSGreg Kroah-Hartman 2298ffdff6aSGreg Kroah-Hartman int insn_inval(struct comedi_device *dev, struct comedi_subdevice *s, 2308ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, unsigned int *data) 2318ffdff6aSGreg Kroah-Hartman { 2328ffdff6aSGreg Kroah-Hartman return -EINVAL; 2338ffdff6aSGreg Kroah-Hartman } 2348ffdff6aSGreg Kroah-Hartman 2358ffdff6aSGreg Kroah-Hartman /** 2368ffdff6aSGreg Kroah-Hartman * comedi_readback_insn_read() - A generic (*insn_read) for subdevice readback. 2378ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 2388ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 2398ffdff6aSGreg Kroah-Hartman * @insn: COMEDI instruction. 2408ffdff6aSGreg Kroah-Hartman * @data: Pointer to return the readback data. 2418ffdff6aSGreg Kroah-Hartman * 2428ffdff6aSGreg Kroah-Hartman * Handles the %INSN_READ instruction for subdevices that use the readback 2438ffdff6aSGreg Kroah-Hartman * array allocated by comedi_alloc_subdev_readback(). It may be used 2448ffdff6aSGreg Kroah-Hartman * directly as the subdevice's handler (@s->insn_read) or called via a 2458ffdff6aSGreg Kroah-Hartman * wrapper. 2468ffdff6aSGreg Kroah-Hartman * 2478ffdff6aSGreg Kroah-Hartman * @insn->n is normally 1, which will read a single value. If higher, the 2488ffdff6aSGreg Kroah-Hartman * same element of the readback array will be read multiple times. 2498ffdff6aSGreg Kroah-Hartman * 2508ffdff6aSGreg Kroah-Hartman * Returns @insn->n on success, or -EINVAL if @s->readback is NULL. 2518ffdff6aSGreg Kroah-Hartman */ 2528ffdff6aSGreg Kroah-Hartman int comedi_readback_insn_read(struct comedi_device *dev, 2538ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 2548ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 2558ffdff6aSGreg Kroah-Hartman unsigned int *data) 2568ffdff6aSGreg Kroah-Hartman { 2578ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec); 2588ffdff6aSGreg Kroah-Hartman int i; 2598ffdff6aSGreg Kroah-Hartman 2608ffdff6aSGreg Kroah-Hartman if (!s->readback) 2618ffdff6aSGreg Kroah-Hartman return -EINVAL; 2628ffdff6aSGreg Kroah-Hartman 2638ffdff6aSGreg Kroah-Hartman for (i = 0; i < insn->n; i++) 2648ffdff6aSGreg Kroah-Hartman data[i] = s->readback[chan]; 2658ffdff6aSGreg Kroah-Hartman 2668ffdff6aSGreg Kroah-Hartman return insn->n; 2678ffdff6aSGreg Kroah-Hartman } 2688ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_readback_insn_read); 2698ffdff6aSGreg Kroah-Hartman 2708ffdff6aSGreg Kroah-Hartman /** 2718ffdff6aSGreg Kroah-Hartman * comedi_timeout() - Busy-wait for a driver condition to occur 2728ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 2738ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 2748ffdff6aSGreg Kroah-Hartman * @insn: COMEDI instruction. 2758ffdff6aSGreg Kroah-Hartman * @cb: Callback to check for the condition. 2768ffdff6aSGreg Kroah-Hartman * @context: Private context from the driver. 2778ffdff6aSGreg Kroah-Hartman * 2788ffdff6aSGreg Kroah-Hartman * Busy-waits for up to a second (%COMEDI_TIMEOUT_MS) for the condition or 2798ffdff6aSGreg Kroah-Hartman * some error (other than -EBUSY) to occur. The parameters @dev, @s, @insn, 2808ffdff6aSGreg Kroah-Hartman * and @context are passed to the callback function, which returns -EBUSY to 2818ffdff6aSGreg Kroah-Hartman * continue waiting or some other value to stop waiting (generally 0 if the 2828ffdff6aSGreg Kroah-Hartman * condition occurred, or some error value). 2838ffdff6aSGreg Kroah-Hartman * 2848ffdff6aSGreg Kroah-Hartman * Returns -ETIMEDOUT if timed out, otherwise the return value from the 2858ffdff6aSGreg Kroah-Hartman * callback function. 2868ffdff6aSGreg Kroah-Hartman */ 2878ffdff6aSGreg Kroah-Hartman int comedi_timeout(struct comedi_device *dev, 2888ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 2898ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 2908ffdff6aSGreg Kroah-Hartman int (*cb)(struct comedi_device *dev, 2918ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 2928ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 2938ffdff6aSGreg Kroah-Hartman unsigned long context), 2948ffdff6aSGreg Kroah-Hartman unsigned long context) 2958ffdff6aSGreg Kroah-Hartman { 2968ffdff6aSGreg Kroah-Hartman unsigned long timeout = jiffies + msecs_to_jiffies(COMEDI_TIMEOUT_MS); 2978ffdff6aSGreg Kroah-Hartman int ret; 2988ffdff6aSGreg Kroah-Hartman 2998ffdff6aSGreg Kroah-Hartman while (time_before(jiffies, timeout)) { 3008ffdff6aSGreg Kroah-Hartman ret = cb(dev, s, insn, context); 3018ffdff6aSGreg Kroah-Hartman if (ret != -EBUSY) 3028ffdff6aSGreg Kroah-Hartman return ret; /* success (0) or non EBUSY errno */ 3038ffdff6aSGreg Kroah-Hartman cpu_relax(); 3048ffdff6aSGreg Kroah-Hartman } 3058ffdff6aSGreg Kroah-Hartman return -ETIMEDOUT; 3068ffdff6aSGreg Kroah-Hartman } 3078ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_timeout); 3088ffdff6aSGreg Kroah-Hartman 3098ffdff6aSGreg Kroah-Hartman /** 3108ffdff6aSGreg Kroah-Hartman * comedi_dio_insn_config() - Boilerplate (*insn_config) for DIO subdevices 3118ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 3128ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 3138ffdff6aSGreg Kroah-Hartman * @insn: COMEDI instruction. 3148ffdff6aSGreg Kroah-Hartman * @data: Instruction parameters and return data. 3158ffdff6aSGreg Kroah-Hartman * @mask: io_bits mask for grouped channels, or 0 for single channel. 3168ffdff6aSGreg Kroah-Hartman * 3178ffdff6aSGreg Kroah-Hartman * If @mask is 0, it is replaced with a single-bit mask corresponding to the 3188ffdff6aSGreg Kroah-Hartman * channel number specified by @insn->chanspec. Otherwise, @mask 3198ffdff6aSGreg Kroah-Hartman * corresponds to a group of channels (which should include the specified 3208ffdff6aSGreg Kroah-Hartman * channel) that are always configured together as inputs or outputs. 3218ffdff6aSGreg Kroah-Hartman * 3228ffdff6aSGreg Kroah-Hartman * Partially handles the %INSN_CONFIG_DIO_INPUT, %INSN_CONFIG_DIO_OUTPUTS, 3238ffdff6aSGreg Kroah-Hartman * and %INSN_CONFIG_DIO_QUERY instructions. The first two update 3248ffdff6aSGreg Kroah-Hartman * @s->io_bits to record the directions of the masked channels. The last 3258ffdff6aSGreg Kroah-Hartman * one sets @data[1] to the current direction of the group of channels 3268ffdff6aSGreg Kroah-Hartman * (%COMEDI_INPUT) or %COMEDI_OUTPUT) as recorded in @s->io_bits. 3278ffdff6aSGreg Kroah-Hartman * 3288ffdff6aSGreg Kroah-Hartman * The caller is responsible for updating the DIO direction in the hardware 3298ffdff6aSGreg Kroah-Hartman * registers if this function returns 0. 3308ffdff6aSGreg Kroah-Hartman * 3318ffdff6aSGreg Kroah-Hartman * Returns 0 for a %INSN_CONFIG_DIO_INPUT or %INSN_CONFIG_DIO_OUTPUT 3328ffdff6aSGreg Kroah-Hartman * instruction, @insn->n (> 0) for a %INSN_CONFIG_DIO_QUERY instruction, or 3338ffdff6aSGreg Kroah-Hartman * -EINVAL for some other instruction. 3348ffdff6aSGreg Kroah-Hartman */ 3358ffdff6aSGreg Kroah-Hartman int comedi_dio_insn_config(struct comedi_device *dev, 3368ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 3378ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 3388ffdff6aSGreg Kroah-Hartman unsigned int *data, 3398ffdff6aSGreg Kroah-Hartman unsigned int mask) 3408ffdff6aSGreg Kroah-Hartman { 3418ffdff6aSGreg Kroah-Hartman unsigned int chan_mask = 1 << CR_CHAN(insn->chanspec); 3428ffdff6aSGreg Kroah-Hartman 3438ffdff6aSGreg Kroah-Hartman if (!mask) 3448ffdff6aSGreg Kroah-Hartman mask = chan_mask; 3458ffdff6aSGreg Kroah-Hartman 3468ffdff6aSGreg Kroah-Hartman switch (data[0]) { 3478ffdff6aSGreg Kroah-Hartman case INSN_CONFIG_DIO_INPUT: 3488ffdff6aSGreg Kroah-Hartman s->io_bits &= ~mask; 3498ffdff6aSGreg Kroah-Hartman break; 3508ffdff6aSGreg Kroah-Hartman 3518ffdff6aSGreg Kroah-Hartman case INSN_CONFIG_DIO_OUTPUT: 3528ffdff6aSGreg Kroah-Hartman s->io_bits |= mask; 3538ffdff6aSGreg Kroah-Hartman break; 3548ffdff6aSGreg Kroah-Hartman 3558ffdff6aSGreg Kroah-Hartman case INSN_CONFIG_DIO_QUERY: 3568ffdff6aSGreg Kroah-Hartman data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT; 3578ffdff6aSGreg Kroah-Hartman return insn->n; 3588ffdff6aSGreg Kroah-Hartman 3598ffdff6aSGreg Kroah-Hartman default: 3608ffdff6aSGreg Kroah-Hartman return -EINVAL; 3618ffdff6aSGreg Kroah-Hartman } 3628ffdff6aSGreg Kroah-Hartman 3638ffdff6aSGreg Kroah-Hartman return 0; 3648ffdff6aSGreg Kroah-Hartman } 3658ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_dio_insn_config); 3668ffdff6aSGreg Kroah-Hartman 3678ffdff6aSGreg Kroah-Hartman /** 3688ffdff6aSGreg Kroah-Hartman * comedi_dio_update_state() - Update the internal state of DIO subdevices 3698ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 3708ffdff6aSGreg Kroah-Hartman * @data: The channel mask and bits to update. 3718ffdff6aSGreg Kroah-Hartman * 3728ffdff6aSGreg Kroah-Hartman * Updates @s->state which holds the internal state of the outputs for DIO 3738ffdff6aSGreg Kroah-Hartman * or DO subdevices (up to 32 channels). @data[0] contains a bit-mask of 3748ffdff6aSGreg Kroah-Hartman * the channels to be updated. @data[1] contains a bit-mask of those 3758ffdff6aSGreg Kroah-Hartman * channels to be set to '1'. The caller is responsible for updating the 3768ffdff6aSGreg Kroah-Hartman * outputs in hardware according to @s->state. As a minimum, the channels 3778ffdff6aSGreg Kroah-Hartman * in the returned bit-mask need to be updated. 3788ffdff6aSGreg Kroah-Hartman * 3798ffdff6aSGreg Kroah-Hartman * Returns @mask with non-existent channels removed. 3808ffdff6aSGreg Kroah-Hartman */ 3818ffdff6aSGreg Kroah-Hartman unsigned int comedi_dio_update_state(struct comedi_subdevice *s, 3828ffdff6aSGreg Kroah-Hartman unsigned int *data) 3838ffdff6aSGreg Kroah-Hartman { 3848ffdff6aSGreg Kroah-Hartman unsigned int chanmask = (s->n_chan < 32) ? ((1 << s->n_chan) - 1) 3858ffdff6aSGreg Kroah-Hartman : 0xffffffff; 3868ffdff6aSGreg Kroah-Hartman unsigned int mask = data[0] & chanmask; 3878ffdff6aSGreg Kroah-Hartman unsigned int bits = data[1]; 3888ffdff6aSGreg Kroah-Hartman 3898ffdff6aSGreg Kroah-Hartman if (mask) { 3908ffdff6aSGreg Kroah-Hartman s->state &= ~mask; 3918ffdff6aSGreg Kroah-Hartman s->state |= (bits & mask); 3928ffdff6aSGreg Kroah-Hartman } 3938ffdff6aSGreg Kroah-Hartman 3948ffdff6aSGreg Kroah-Hartman return mask; 3958ffdff6aSGreg Kroah-Hartman } 3968ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_dio_update_state); 3978ffdff6aSGreg Kroah-Hartman 3988ffdff6aSGreg Kroah-Hartman /** 3998ffdff6aSGreg Kroah-Hartman * comedi_bytes_per_scan_cmd() - Get length of asynchronous command "scan" in 4008ffdff6aSGreg Kroah-Hartman * bytes 4018ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 4028ffdff6aSGreg Kroah-Hartman * @cmd: COMEDI command. 4038ffdff6aSGreg Kroah-Hartman * 4048ffdff6aSGreg Kroah-Hartman * Determines the overall scan length according to the subdevice type and the 4058ffdff6aSGreg Kroah-Hartman * number of channels in the scan for the specified command. 4068ffdff6aSGreg Kroah-Hartman * 4078ffdff6aSGreg Kroah-Hartman * For digital input, output or input/output subdevices, samples for 4088ffdff6aSGreg Kroah-Hartman * multiple channels are assumed to be packed into one or more unsigned 4098ffdff6aSGreg Kroah-Hartman * short or unsigned int values according to the subdevice's %SDF_LSAMPL 4108ffdff6aSGreg Kroah-Hartman * flag. For other types of subdevice, samples are assumed to occupy a 4118ffdff6aSGreg Kroah-Hartman * whole unsigned short or unsigned int according to the %SDF_LSAMPL flag. 4128ffdff6aSGreg Kroah-Hartman * 4138ffdff6aSGreg Kroah-Hartman * Returns the overall scan length in bytes. 4148ffdff6aSGreg Kroah-Hartman */ 4158ffdff6aSGreg Kroah-Hartman unsigned int comedi_bytes_per_scan_cmd(struct comedi_subdevice *s, 4168ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd) 4178ffdff6aSGreg Kroah-Hartman { 4188ffdff6aSGreg Kroah-Hartman unsigned int num_samples; 4198ffdff6aSGreg Kroah-Hartman unsigned int bits_per_sample; 4208ffdff6aSGreg Kroah-Hartman 4218ffdff6aSGreg Kroah-Hartman switch (s->type) { 4228ffdff6aSGreg Kroah-Hartman case COMEDI_SUBD_DI: 4238ffdff6aSGreg Kroah-Hartman case COMEDI_SUBD_DO: 4248ffdff6aSGreg Kroah-Hartman case COMEDI_SUBD_DIO: 4258ffdff6aSGreg Kroah-Hartman bits_per_sample = 8 * comedi_bytes_per_sample(s); 4268ffdff6aSGreg Kroah-Hartman num_samples = DIV_ROUND_UP(cmd->scan_end_arg, bits_per_sample); 4278ffdff6aSGreg Kroah-Hartman break; 4288ffdff6aSGreg Kroah-Hartman default: 4298ffdff6aSGreg Kroah-Hartman num_samples = cmd->scan_end_arg; 4308ffdff6aSGreg Kroah-Hartman break; 4318ffdff6aSGreg Kroah-Hartman } 4328ffdff6aSGreg Kroah-Hartman return comedi_samples_to_bytes(s, num_samples); 4338ffdff6aSGreg Kroah-Hartman } 4348ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_bytes_per_scan_cmd); 4358ffdff6aSGreg Kroah-Hartman 4368ffdff6aSGreg Kroah-Hartman /** 4378ffdff6aSGreg Kroah-Hartman * comedi_bytes_per_scan() - Get length of asynchronous command "scan" in bytes 4388ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 4398ffdff6aSGreg Kroah-Hartman * 4408ffdff6aSGreg Kroah-Hartman * Determines the overall scan length according to the subdevice type and the 4418ffdff6aSGreg Kroah-Hartman * number of channels in the scan for the current command. 4428ffdff6aSGreg Kroah-Hartman * 4438ffdff6aSGreg Kroah-Hartman * For digital input, output or input/output subdevices, samples for 4448ffdff6aSGreg Kroah-Hartman * multiple channels are assumed to be packed into one or more unsigned 4458ffdff6aSGreg Kroah-Hartman * short or unsigned int values according to the subdevice's %SDF_LSAMPL 4468ffdff6aSGreg Kroah-Hartman * flag. For other types of subdevice, samples are assumed to occupy a 4478ffdff6aSGreg Kroah-Hartman * whole unsigned short or unsigned int according to the %SDF_LSAMPL flag. 4488ffdff6aSGreg Kroah-Hartman * 4498ffdff6aSGreg Kroah-Hartman * Returns the overall scan length in bytes. 4508ffdff6aSGreg Kroah-Hartman */ 4518ffdff6aSGreg Kroah-Hartman unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s) 4528ffdff6aSGreg Kroah-Hartman { 4538ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &s->async->cmd; 4548ffdff6aSGreg Kroah-Hartman 4558ffdff6aSGreg Kroah-Hartman return comedi_bytes_per_scan_cmd(s, cmd); 4568ffdff6aSGreg Kroah-Hartman } 4578ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_bytes_per_scan); 4588ffdff6aSGreg Kroah-Hartman 4598ffdff6aSGreg Kroah-Hartman static unsigned int __comedi_nscans_left(struct comedi_subdevice *s, 4608ffdff6aSGreg Kroah-Hartman unsigned int nscans) 4618ffdff6aSGreg Kroah-Hartman { 4628ffdff6aSGreg Kroah-Hartman struct comedi_async *async = s->async; 4638ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &async->cmd; 4648ffdff6aSGreg Kroah-Hartman 4658ffdff6aSGreg Kroah-Hartman if (cmd->stop_src == TRIG_COUNT) { 4668ffdff6aSGreg Kroah-Hartman unsigned int scans_left = 0; 4678ffdff6aSGreg Kroah-Hartman 4688ffdff6aSGreg Kroah-Hartman if (async->scans_done < cmd->stop_arg) 4698ffdff6aSGreg Kroah-Hartman scans_left = cmd->stop_arg - async->scans_done; 4708ffdff6aSGreg Kroah-Hartman 4718ffdff6aSGreg Kroah-Hartman if (nscans > scans_left) 4728ffdff6aSGreg Kroah-Hartman nscans = scans_left; 4738ffdff6aSGreg Kroah-Hartman } 4748ffdff6aSGreg Kroah-Hartman return nscans; 4758ffdff6aSGreg Kroah-Hartman } 4768ffdff6aSGreg Kroah-Hartman 4778ffdff6aSGreg Kroah-Hartman /** 4788ffdff6aSGreg Kroah-Hartman * comedi_nscans_left() - Return the number of scans left in the command 4798ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 4808ffdff6aSGreg Kroah-Hartman * @nscans: The expected number of scans or 0 for all available scans. 4818ffdff6aSGreg Kroah-Hartman * 4828ffdff6aSGreg Kroah-Hartman * If @nscans is 0, it is set to the number of scans available in the 4838ffdff6aSGreg Kroah-Hartman * async buffer. 4848ffdff6aSGreg Kroah-Hartman * 4858ffdff6aSGreg Kroah-Hartman * If the async command has a stop_src of %TRIG_COUNT, the @nscans will be 4868ffdff6aSGreg Kroah-Hartman * checked against the number of scans remaining to complete the command. 4878ffdff6aSGreg Kroah-Hartman * 4888ffdff6aSGreg Kroah-Hartman * The return value will then be either the expected number of scans or the 4898ffdff6aSGreg Kroah-Hartman * number of scans remaining to complete the command, whichever is fewer. 4908ffdff6aSGreg Kroah-Hartman */ 4918ffdff6aSGreg Kroah-Hartman unsigned int comedi_nscans_left(struct comedi_subdevice *s, 4928ffdff6aSGreg Kroah-Hartman unsigned int nscans) 4938ffdff6aSGreg Kroah-Hartman { 4948ffdff6aSGreg Kroah-Hartman if (nscans == 0) { 4958ffdff6aSGreg Kroah-Hartman unsigned int nbytes = comedi_buf_read_n_available(s); 4968ffdff6aSGreg Kroah-Hartman 4978ffdff6aSGreg Kroah-Hartman nscans = nbytes / comedi_bytes_per_scan(s); 4988ffdff6aSGreg Kroah-Hartman } 4998ffdff6aSGreg Kroah-Hartman return __comedi_nscans_left(s, nscans); 5008ffdff6aSGreg Kroah-Hartman } 5018ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_nscans_left); 5028ffdff6aSGreg Kroah-Hartman 5038ffdff6aSGreg Kroah-Hartman /** 5048ffdff6aSGreg Kroah-Hartman * comedi_nsamples_left() - Return the number of samples left in the command 5058ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 5068ffdff6aSGreg Kroah-Hartman * @nsamples: The expected number of samples. 5078ffdff6aSGreg Kroah-Hartman * 5088ffdff6aSGreg Kroah-Hartman * Returns the number of samples remaining to complete the command, or the 5098ffdff6aSGreg Kroah-Hartman * specified expected number of samples (@nsamples), whichever is fewer. 5108ffdff6aSGreg Kroah-Hartman */ 5118ffdff6aSGreg Kroah-Hartman unsigned int comedi_nsamples_left(struct comedi_subdevice *s, 5128ffdff6aSGreg Kroah-Hartman unsigned int nsamples) 5138ffdff6aSGreg Kroah-Hartman { 5148ffdff6aSGreg Kroah-Hartman struct comedi_async *async = s->async; 5158ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &async->cmd; 5168ffdff6aSGreg Kroah-Hartman unsigned long long scans_left; 5178ffdff6aSGreg Kroah-Hartman unsigned long long samples_left; 5188ffdff6aSGreg Kroah-Hartman 5198ffdff6aSGreg Kroah-Hartman if (cmd->stop_src != TRIG_COUNT) 5208ffdff6aSGreg Kroah-Hartman return nsamples; 5218ffdff6aSGreg Kroah-Hartman 5228ffdff6aSGreg Kroah-Hartman scans_left = __comedi_nscans_left(s, cmd->stop_arg); 5238ffdff6aSGreg Kroah-Hartman if (!scans_left) 5248ffdff6aSGreg Kroah-Hartman return 0; 5258ffdff6aSGreg Kroah-Hartman 5268ffdff6aSGreg Kroah-Hartman samples_left = scans_left * cmd->scan_end_arg - 5278ffdff6aSGreg Kroah-Hartman comedi_bytes_to_samples(s, async->scan_progress); 5288ffdff6aSGreg Kroah-Hartman 5298ffdff6aSGreg Kroah-Hartman if (samples_left < nsamples) 5308ffdff6aSGreg Kroah-Hartman return samples_left; 5318ffdff6aSGreg Kroah-Hartman return nsamples; 5328ffdff6aSGreg Kroah-Hartman } 5338ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_nsamples_left); 5348ffdff6aSGreg Kroah-Hartman 5358ffdff6aSGreg Kroah-Hartman /** 5368ffdff6aSGreg Kroah-Hartman * comedi_inc_scan_progress() - Update scan progress in asynchronous command 5378ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 5388ffdff6aSGreg Kroah-Hartman * @num_bytes: Amount of data in bytes to increment scan progress. 5398ffdff6aSGreg Kroah-Hartman * 5408ffdff6aSGreg Kroah-Hartman * Increments the scan progress by the number of bytes specified by @num_bytes. 5418ffdff6aSGreg Kroah-Hartman * If the scan progress reaches or exceeds the scan length in bytes, reduce 5428ffdff6aSGreg Kroah-Hartman * it modulo the scan length in bytes and set the "end of scan" asynchronous 5438ffdff6aSGreg Kroah-Hartman * event flag (%COMEDI_CB_EOS) to be processed later. 5448ffdff6aSGreg Kroah-Hartman */ 5458ffdff6aSGreg Kroah-Hartman void comedi_inc_scan_progress(struct comedi_subdevice *s, 5468ffdff6aSGreg Kroah-Hartman unsigned int num_bytes) 5478ffdff6aSGreg Kroah-Hartman { 5488ffdff6aSGreg Kroah-Hartman struct comedi_async *async = s->async; 5498ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &async->cmd; 5508ffdff6aSGreg Kroah-Hartman unsigned int scan_length = comedi_bytes_per_scan(s); 5518ffdff6aSGreg Kroah-Hartman 5528ffdff6aSGreg Kroah-Hartman /* track the 'cur_chan' for non-SDF_PACKED subdevices */ 5538ffdff6aSGreg Kroah-Hartman if (!(s->subdev_flags & SDF_PACKED)) { 5548ffdff6aSGreg Kroah-Hartman async->cur_chan += comedi_bytes_to_samples(s, num_bytes); 5558ffdff6aSGreg Kroah-Hartman async->cur_chan %= cmd->chanlist_len; 5568ffdff6aSGreg Kroah-Hartman } 5578ffdff6aSGreg Kroah-Hartman 5588ffdff6aSGreg Kroah-Hartman async->scan_progress += num_bytes; 5598ffdff6aSGreg Kroah-Hartman if (async->scan_progress >= scan_length) { 5608ffdff6aSGreg Kroah-Hartman unsigned int nscans = async->scan_progress / scan_length; 5618ffdff6aSGreg Kroah-Hartman 5628ffdff6aSGreg Kroah-Hartman if (async->scans_done < (UINT_MAX - nscans)) 5638ffdff6aSGreg Kroah-Hartman async->scans_done += nscans; 5648ffdff6aSGreg Kroah-Hartman else 5658ffdff6aSGreg Kroah-Hartman async->scans_done = UINT_MAX; 5668ffdff6aSGreg Kroah-Hartman 5678ffdff6aSGreg Kroah-Hartman async->scan_progress %= scan_length; 5688ffdff6aSGreg Kroah-Hartman async->events |= COMEDI_CB_EOS; 5698ffdff6aSGreg Kroah-Hartman } 5708ffdff6aSGreg Kroah-Hartman } 5718ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_inc_scan_progress); 5728ffdff6aSGreg Kroah-Hartman 5738ffdff6aSGreg Kroah-Hartman /** 5748ffdff6aSGreg Kroah-Hartman * comedi_handle_events() - Handle events and possibly stop acquisition 5758ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 5768ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 5778ffdff6aSGreg Kroah-Hartman * 5788ffdff6aSGreg Kroah-Hartman * Handles outstanding asynchronous acquisition event flags associated 5798ffdff6aSGreg Kroah-Hartman * with the subdevice. Call the subdevice's @s->cancel() handler if the 5808ffdff6aSGreg Kroah-Hartman * "end of acquisition", "error" or "overflow" event flags are set in order 5818ffdff6aSGreg Kroah-Hartman * to stop the acquisition at the driver level. 5828ffdff6aSGreg Kroah-Hartman * 5838ffdff6aSGreg Kroah-Hartman * Calls comedi_event() to further process the event flags, which may mark 5848ffdff6aSGreg Kroah-Hartman * the asynchronous command as no longer running, possibly terminated with 5858ffdff6aSGreg Kroah-Hartman * an error, and may wake up tasks. 5868ffdff6aSGreg Kroah-Hartman * 5878ffdff6aSGreg Kroah-Hartman * Return a bit-mask of the handled events. 5888ffdff6aSGreg Kroah-Hartman */ 5898ffdff6aSGreg Kroah-Hartman unsigned int comedi_handle_events(struct comedi_device *dev, 5908ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s) 5918ffdff6aSGreg Kroah-Hartman { 5928ffdff6aSGreg Kroah-Hartman unsigned int events = s->async->events; 5938ffdff6aSGreg Kroah-Hartman 5948ffdff6aSGreg Kroah-Hartman if (events == 0) 5958ffdff6aSGreg Kroah-Hartman return events; 5968ffdff6aSGreg Kroah-Hartman 5978ffdff6aSGreg Kroah-Hartman if ((events & COMEDI_CB_CANCEL_MASK) && s->cancel) 5988ffdff6aSGreg Kroah-Hartman s->cancel(dev, s); 5998ffdff6aSGreg Kroah-Hartman 6008ffdff6aSGreg Kroah-Hartman comedi_event(dev, s); 6018ffdff6aSGreg Kroah-Hartman 6028ffdff6aSGreg Kroah-Hartman return events; 6038ffdff6aSGreg Kroah-Hartman } 6048ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_handle_events); 6058ffdff6aSGreg Kroah-Hartman 6068ffdff6aSGreg Kroah-Hartman static int insn_rw_emulate_bits(struct comedi_device *dev, 6078ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 6088ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 6098ffdff6aSGreg Kroah-Hartman unsigned int *data) 6108ffdff6aSGreg Kroah-Hartman { 6118ffdff6aSGreg Kroah-Hartman struct comedi_insn _insn; 6128ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec); 6138ffdff6aSGreg Kroah-Hartman unsigned int base_chan = (chan < 32) ? 0 : chan; 6148ffdff6aSGreg Kroah-Hartman unsigned int _data[2]; 6158ffdff6aSGreg Kroah-Hartman int ret; 6168ffdff6aSGreg Kroah-Hartman 6178ffdff6aSGreg Kroah-Hartman memset(_data, 0, sizeof(_data)); 6188ffdff6aSGreg Kroah-Hartman memset(&_insn, 0, sizeof(_insn)); 6198ffdff6aSGreg Kroah-Hartman _insn.insn = INSN_BITS; 6208ffdff6aSGreg Kroah-Hartman _insn.chanspec = base_chan; 6218ffdff6aSGreg Kroah-Hartman _insn.n = 2; 6228ffdff6aSGreg Kroah-Hartman _insn.subdev = insn->subdev; 6238ffdff6aSGreg Kroah-Hartman 6248ffdff6aSGreg Kroah-Hartman if (insn->insn == INSN_WRITE) { 6258ffdff6aSGreg Kroah-Hartman if (!(s->subdev_flags & SDF_WRITABLE)) 6268ffdff6aSGreg Kroah-Hartman return -EINVAL; 6278ffdff6aSGreg Kroah-Hartman _data[0] = 1 << (chan - base_chan); /* mask */ 6288ffdff6aSGreg Kroah-Hartman _data[1] = data[0] ? (1 << (chan - base_chan)) : 0; /* bits */ 6298ffdff6aSGreg Kroah-Hartman } 6308ffdff6aSGreg Kroah-Hartman 6318ffdff6aSGreg Kroah-Hartman ret = s->insn_bits(dev, s, &_insn, _data); 6328ffdff6aSGreg Kroah-Hartman if (ret < 0) 6338ffdff6aSGreg Kroah-Hartman return ret; 6348ffdff6aSGreg Kroah-Hartman 6358ffdff6aSGreg Kroah-Hartman if (insn->insn == INSN_READ) 6368ffdff6aSGreg Kroah-Hartman data[0] = (_data[1] >> (chan - base_chan)) & 1; 6378ffdff6aSGreg Kroah-Hartman 6388ffdff6aSGreg Kroah-Hartman return 1; 6398ffdff6aSGreg Kroah-Hartman } 6408ffdff6aSGreg Kroah-Hartman 6418ffdff6aSGreg Kroah-Hartman static int __comedi_device_postconfig_async(struct comedi_device *dev, 6428ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s) 6438ffdff6aSGreg Kroah-Hartman { 6448ffdff6aSGreg Kroah-Hartman struct comedi_async *async; 6458ffdff6aSGreg Kroah-Hartman unsigned int buf_size; 6468ffdff6aSGreg Kroah-Hartman int ret; 6478ffdff6aSGreg Kroah-Hartman 6488ffdff6aSGreg Kroah-Hartman lockdep_assert_held(&dev->mutex); 6498ffdff6aSGreg Kroah-Hartman if ((s->subdev_flags & (SDF_CMD_READ | SDF_CMD_WRITE)) == 0) { 6508ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, 6518ffdff6aSGreg Kroah-Hartman "async subdevices must support SDF_CMD_READ or SDF_CMD_WRITE\n"); 6528ffdff6aSGreg Kroah-Hartman return -EINVAL; 6538ffdff6aSGreg Kroah-Hartman } 6548ffdff6aSGreg Kroah-Hartman if (!s->do_cmdtest) { 6558ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, 6568ffdff6aSGreg Kroah-Hartman "async subdevices must have a do_cmdtest() function\n"); 6578ffdff6aSGreg Kroah-Hartman return -EINVAL; 6588ffdff6aSGreg Kroah-Hartman } 6598ffdff6aSGreg Kroah-Hartman if (!s->cancel) 6608ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, 6618ffdff6aSGreg Kroah-Hartman "async subdevices should have a cancel() function\n"); 6628ffdff6aSGreg Kroah-Hartman 6638ffdff6aSGreg Kroah-Hartman async = kzalloc(sizeof(*async), GFP_KERNEL); 6648ffdff6aSGreg Kroah-Hartman if (!async) 6658ffdff6aSGreg Kroah-Hartman return -ENOMEM; 6668ffdff6aSGreg Kroah-Hartman 6678ffdff6aSGreg Kroah-Hartman init_waitqueue_head(&async->wait_head); 6688ffdff6aSGreg Kroah-Hartman s->async = async; 6698ffdff6aSGreg Kroah-Hartman 6708ffdff6aSGreg Kroah-Hartman async->max_bufsize = comedi_default_buf_maxsize_kb * 1024; 6718ffdff6aSGreg Kroah-Hartman buf_size = comedi_default_buf_size_kb * 1024; 6728ffdff6aSGreg Kroah-Hartman if (buf_size > async->max_bufsize) 6738ffdff6aSGreg Kroah-Hartman buf_size = async->max_bufsize; 6748ffdff6aSGreg Kroah-Hartman 6758ffdff6aSGreg Kroah-Hartman if (comedi_buf_alloc(dev, s, buf_size) < 0) { 6768ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, "Buffer allocation failed\n"); 6778ffdff6aSGreg Kroah-Hartman return -ENOMEM; 6788ffdff6aSGreg Kroah-Hartman } 6798ffdff6aSGreg Kroah-Hartman if (s->buf_change) { 6808ffdff6aSGreg Kroah-Hartman ret = s->buf_change(dev, s); 6818ffdff6aSGreg Kroah-Hartman if (ret < 0) 6828ffdff6aSGreg Kroah-Hartman return ret; 6838ffdff6aSGreg Kroah-Hartman } 6848ffdff6aSGreg Kroah-Hartman 6858ffdff6aSGreg Kroah-Hartman comedi_alloc_subdevice_minor(s); 6868ffdff6aSGreg Kroah-Hartman 6878ffdff6aSGreg Kroah-Hartman return 0; 6888ffdff6aSGreg Kroah-Hartman } 6898ffdff6aSGreg Kroah-Hartman 6908ffdff6aSGreg Kroah-Hartman static int __comedi_device_postconfig(struct comedi_device *dev) 6918ffdff6aSGreg Kroah-Hartman { 6928ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s; 6938ffdff6aSGreg Kroah-Hartman int ret; 6948ffdff6aSGreg Kroah-Hartman int i; 6958ffdff6aSGreg Kroah-Hartman 6968ffdff6aSGreg Kroah-Hartman lockdep_assert_held(&dev->mutex); 6978ffdff6aSGreg Kroah-Hartman if (!dev->insn_device_config) 6988ffdff6aSGreg Kroah-Hartman dev->insn_device_config = insn_device_inval; 6998ffdff6aSGreg Kroah-Hartman 7008ffdff6aSGreg Kroah-Hartman if (!dev->get_valid_routes) 7018ffdff6aSGreg Kroah-Hartman dev->get_valid_routes = get_zero_valid_routes; 7028ffdff6aSGreg Kroah-Hartman 7038ffdff6aSGreg Kroah-Hartman for (i = 0; i < dev->n_subdevices; i++) { 7048ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[i]; 7058ffdff6aSGreg Kroah-Hartman 7068ffdff6aSGreg Kroah-Hartman if (s->type == COMEDI_SUBD_UNUSED) 7078ffdff6aSGreg Kroah-Hartman continue; 7088ffdff6aSGreg Kroah-Hartman 7098ffdff6aSGreg Kroah-Hartman if (s->type == COMEDI_SUBD_DO) { 7108ffdff6aSGreg Kroah-Hartman if (s->n_chan < 32) 7118ffdff6aSGreg Kroah-Hartman s->io_bits = (1 << s->n_chan) - 1; 7128ffdff6aSGreg Kroah-Hartman else 7138ffdff6aSGreg Kroah-Hartman s->io_bits = 0xffffffff; 7148ffdff6aSGreg Kroah-Hartman } 7158ffdff6aSGreg Kroah-Hartman 7168ffdff6aSGreg Kroah-Hartman if (s->len_chanlist == 0) 7178ffdff6aSGreg Kroah-Hartman s->len_chanlist = 1; 7188ffdff6aSGreg Kroah-Hartman 7198ffdff6aSGreg Kroah-Hartman if (s->do_cmd) { 7208ffdff6aSGreg Kroah-Hartman ret = __comedi_device_postconfig_async(dev, s); 7218ffdff6aSGreg Kroah-Hartman if (ret) 7228ffdff6aSGreg Kroah-Hartman return ret; 7238ffdff6aSGreg Kroah-Hartman } 7248ffdff6aSGreg Kroah-Hartman 7258ffdff6aSGreg Kroah-Hartman if (!s->range_table && !s->range_table_list) 7268ffdff6aSGreg Kroah-Hartman s->range_table = &range_unknown; 7278ffdff6aSGreg Kroah-Hartman 7288ffdff6aSGreg Kroah-Hartman if (!s->insn_read && s->insn_bits) 7298ffdff6aSGreg Kroah-Hartman s->insn_read = insn_rw_emulate_bits; 7308ffdff6aSGreg Kroah-Hartman if (!s->insn_write && s->insn_bits) 7318ffdff6aSGreg Kroah-Hartman s->insn_write = insn_rw_emulate_bits; 7328ffdff6aSGreg Kroah-Hartman 7338ffdff6aSGreg Kroah-Hartman if (!s->insn_read) 7348ffdff6aSGreg Kroah-Hartman s->insn_read = insn_inval; 7358ffdff6aSGreg Kroah-Hartman if (!s->insn_write) 7368ffdff6aSGreg Kroah-Hartman s->insn_write = insn_inval; 7378ffdff6aSGreg Kroah-Hartman if (!s->insn_bits) 7388ffdff6aSGreg Kroah-Hartman s->insn_bits = insn_inval; 7398ffdff6aSGreg Kroah-Hartman if (!s->insn_config) 7408ffdff6aSGreg Kroah-Hartman s->insn_config = insn_inval; 7418ffdff6aSGreg Kroah-Hartman 7428ffdff6aSGreg Kroah-Hartman if (!s->poll) 7438ffdff6aSGreg Kroah-Hartman s->poll = poll_invalid; 7448ffdff6aSGreg Kroah-Hartman } 7458ffdff6aSGreg Kroah-Hartman 7468ffdff6aSGreg Kroah-Hartman return 0; 7478ffdff6aSGreg Kroah-Hartman } 7488ffdff6aSGreg Kroah-Hartman 7498ffdff6aSGreg Kroah-Hartman /* do a little post-config cleanup */ 7508ffdff6aSGreg Kroah-Hartman static int comedi_device_postconfig(struct comedi_device *dev) 7518ffdff6aSGreg Kroah-Hartman { 7528ffdff6aSGreg Kroah-Hartman int ret; 7538ffdff6aSGreg Kroah-Hartman 7548ffdff6aSGreg Kroah-Hartman lockdep_assert_held(&dev->mutex); 7558ffdff6aSGreg Kroah-Hartman ret = __comedi_device_postconfig(dev); 7568ffdff6aSGreg Kroah-Hartman if (ret < 0) 7578ffdff6aSGreg Kroah-Hartman return ret; 7588ffdff6aSGreg Kroah-Hartman down_write(&dev->attach_lock); 7598ffdff6aSGreg Kroah-Hartman dev->attached = true; 7608ffdff6aSGreg Kroah-Hartman up_write(&dev->attach_lock); 7618ffdff6aSGreg Kroah-Hartman return 0; 7628ffdff6aSGreg Kroah-Hartman } 7638ffdff6aSGreg Kroah-Hartman 7648ffdff6aSGreg Kroah-Hartman /* 7658ffdff6aSGreg Kroah-Hartman * Generic recognize function for drivers that register their supported 7668ffdff6aSGreg Kroah-Hartman * board names. 7678ffdff6aSGreg Kroah-Hartman * 7688ffdff6aSGreg Kroah-Hartman * 'driv->board_name' points to a 'const char *' member within the 7698ffdff6aSGreg Kroah-Hartman * zeroth element of an array of some private board information 7708ffdff6aSGreg Kroah-Hartman * structure, say 'struct foo_board' containing a member 'const char 7718ffdff6aSGreg Kroah-Hartman * *board_name' that is initialized to point to a board name string that 7728ffdff6aSGreg Kroah-Hartman * is one of the candidates matched against this function's 'name' 7738ffdff6aSGreg Kroah-Hartman * parameter. 7748ffdff6aSGreg Kroah-Hartman * 7758ffdff6aSGreg Kroah-Hartman * 'driv->offset' is the size of the private board information 7768ffdff6aSGreg Kroah-Hartman * structure, say 'sizeof(struct foo_board)', and 'driv->num_names' is 7778ffdff6aSGreg Kroah-Hartman * the length of the array of private board information structures. 7788ffdff6aSGreg Kroah-Hartman * 7798ffdff6aSGreg Kroah-Hartman * If one of the board names in the array of private board information 7808ffdff6aSGreg Kroah-Hartman * structures matches the name supplied to this function, the function 7818ffdff6aSGreg Kroah-Hartman * returns a pointer to the pointer to the board name, otherwise it 7828ffdff6aSGreg Kroah-Hartman * returns NULL. The return value ends up in the 'board_ptr' member of 7838ffdff6aSGreg Kroah-Hartman * a 'struct comedi_device' that the low-level comedi driver's 7848ffdff6aSGreg Kroah-Hartman * 'attach()' hook can convert to a point to a particular element of its 7858ffdff6aSGreg Kroah-Hartman * array of private board information structures by subtracting the 7868ffdff6aSGreg Kroah-Hartman * offset of the member that points to the board name. (No subtraction 7878ffdff6aSGreg Kroah-Hartman * is required if the board name pointer is the first member of the 7888ffdff6aSGreg Kroah-Hartman * private board information structure, which is generally the case.) 7898ffdff6aSGreg Kroah-Hartman */ 7908ffdff6aSGreg Kroah-Hartman static void *comedi_recognize(struct comedi_driver *driv, const char *name) 7918ffdff6aSGreg Kroah-Hartman { 7928ffdff6aSGreg Kroah-Hartman char **name_ptr = (char **)driv->board_name; 7938ffdff6aSGreg Kroah-Hartman int i; 7948ffdff6aSGreg Kroah-Hartman 7958ffdff6aSGreg Kroah-Hartman for (i = 0; i < driv->num_names; i++) { 7968ffdff6aSGreg Kroah-Hartman if (strcmp(*name_ptr, name) == 0) 7978ffdff6aSGreg Kroah-Hartman return name_ptr; 7988ffdff6aSGreg Kroah-Hartman name_ptr = (void *)name_ptr + driv->offset; 7998ffdff6aSGreg Kroah-Hartman } 8008ffdff6aSGreg Kroah-Hartman 8018ffdff6aSGreg Kroah-Hartman return NULL; 8028ffdff6aSGreg Kroah-Hartman } 8038ffdff6aSGreg Kroah-Hartman 8048ffdff6aSGreg Kroah-Hartman static void comedi_report_boards(struct comedi_driver *driv) 8058ffdff6aSGreg Kroah-Hartman { 8068ffdff6aSGreg Kroah-Hartman unsigned int i; 8078ffdff6aSGreg Kroah-Hartman const char *const *name_ptr; 8088ffdff6aSGreg Kroah-Hartman 8098ffdff6aSGreg Kroah-Hartman pr_info("comedi: valid board names for %s driver are:\n", 8108ffdff6aSGreg Kroah-Hartman driv->driver_name); 8118ffdff6aSGreg Kroah-Hartman 8128ffdff6aSGreg Kroah-Hartman name_ptr = driv->board_name; 8138ffdff6aSGreg Kroah-Hartman for (i = 0; i < driv->num_names; i++) { 8148ffdff6aSGreg Kroah-Hartman pr_info(" %s\n", *name_ptr); 8158ffdff6aSGreg Kroah-Hartman name_ptr = (const char **)((char *)name_ptr + driv->offset); 8168ffdff6aSGreg Kroah-Hartman } 8178ffdff6aSGreg Kroah-Hartman 8188ffdff6aSGreg Kroah-Hartman if (driv->num_names == 0) 8198ffdff6aSGreg Kroah-Hartman pr_info(" %s\n", driv->driver_name); 8208ffdff6aSGreg Kroah-Hartman } 8218ffdff6aSGreg Kroah-Hartman 8228ffdff6aSGreg Kroah-Hartman /** 8238ffdff6aSGreg Kroah-Hartman * comedi_load_firmware() - Request and load firmware for a device 8248ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 8258ffdff6aSGreg Kroah-Hartman * @device: Hardware device. 8268ffdff6aSGreg Kroah-Hartman * @name: The name of the firmware image. 8278ffdff6aSGreg Kroah-Hartman * @cb: Callback to the upload the firmware image. 8288ffdff6aSGreg Kroah-Hartman * @context: Private context from the driver. 8298ffdff6aSGreg Kroah-Hartman * 8308ffdff6aSGreg Kroah-Hartman * Sends a firmware request for the hardware device and waits for it. Calls 8318ffdff6aSGreg Kroah-Hartman * the callback function to upload the firmware to the device, them releases 8328ffdff6aSGreg Kroah-Hartman * the firmware. 8338ffdff6aSGreg Kroah-Hartman * 8348ffdff6aSGreg Kroah-Hartman * Returns 0 on success, -EINVAL if @cb is NULL, or a negative error number 8358ffdff6aSGreg Kroah-Hartman * from the firmware request or the callback function. 8368ffdff6aSGreg Kroah-Hartman */ 8378ffdff6aSGreg Kroah-Hartman int comedi_load_firmware(struct comedi_device *dev, 8388ffdff6aSGreg Kroah-Hartman struct device *device, 8398ffdff6aSGreg Kroah-Hartman const char *name, 8408ffdff6aSGreg Kroah-Hartman int (*cb)(struct comedi_device *dev, 8418ffdff6aSGreg Kroah-Hartman const u8 *data, size_t size, 8428ffdff6aSGreg Kroah-Hartman unsigned long context), 8438ffdff6aSGreg Kroah-Hartman unsigned long context) 8448ffdff6aSGreg Kroah-Hartman { 8458ffdff6aSGreg Kroah-Hartman const struct firmware *fw; 8468ffdff6aSGreg Kroah-Hartman int ret; 8478ffdff6aSGreg Kroah-Hartman 8488ffdff6aSGreg Kroah-Hartman if (!cb) 8498ffdff6aSGreg Kroah-Hartman return -EINVAL; 8508ffdff6aSGreg Kroah-Hartman 8518ffdff6aSGreg Kroah-Hartman ret = request_firmware(&fw, name, device); 8528ffdff6aSGreg Kroah-Hartman if (ret == 0) { 8538ffdff6aSGreg Kroah-Hartman ret = cb(dev, fw->data, fw->size, context); 8548ffdff6aSGreg Kroah-Hartman release_firmware(fw); 8558ffdff6aSGreg Kroah-Hartman } 8568ffdff6aSGreg Kroah-Hartman 8578ffdff6aSGreg Kroah-Hartman return ret < 0 ? ret : 0; 8588ffdff6aSGreg Kroah-Hartman } 8598ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_load_firmware); 8608ffdff6aSGreg Kroah-Hartman 8618ffdff6aSGreg Kroah-Hartman /** 8628ffdff6aSGreg Kroah-Hartman * __comedi_request_region() - Request an I/O region for a legacy driver 8638ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 8648ffdff6aSGreg Kroah-Hartman * @start: Base address of the I/O region. 8658ffdff6aSGreg Kroah-Hartman * @len: Length of the I/O region. 8668ffdff6aSGreg Kroah-Hartman * 8678ffdff6aSGreg Kroah-Hartman * Requests the specified I/O port region which must start at a non-zero 8688ffdff6aSGreg Kroah-Hartman * address. 8698ffdff6aSGreg Kroah-Hartman * 8708ffdff6aSGreg Kroah-Hartman * Returns 0 on success, -EINVAL if @start is 0, or -EIO if the request 8718ffdff6aSGreg Kroah-Hartman * fails. 8728ffdff6aSGreg Kroah-Hartman */ 8738ffdff6aSGreg Kroah-Hartman int __comedi_request_region(struct comedi_device *dev, 8748ffdff6aSGreg Kroah-Hartman unsigned long start, unsigned long len) 8758ffdff6aSGreg Kroah-Hartman { 8768ffdff6aSGreg Kroah-Hartman if (!start) { 8778ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, 8788ffdff6aSGreg Kroah-Hartman "%s: a I/O base address must be specified\n", 8798ffdff6aSGreg Kroah-Hartman dev->board_name); 8808ffdff6aSGreg Kroah-Hartman return -EINVAL; 8818ffdff6aSGreg Kroah-Hartman } 8828ffdff6aSGreg Kroah-Hartman 8838ffdff6aSGreg Kroah-Hartman if (!request_region(start, len, dev->board_name)) { 8848ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, "%s: I/O port conflict (%#lx,%lu)\n", 8858ffdff6aSGreg Kroah-Hartman dev->board_name, start, len); 8868ffdff6aSGreg Kroah-Hartman return -EIO; 8878ffdff6aSGreg Kroah-Hartman } 8888ffdff6aSGreg Kroah-Hartman 8898ffdff6aSGreg Kroah-Hartman return 0; 8908ffdff6aSGreg Kroah-Hartman } 8918ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(__comedi_request_region); 8928ffdff6aSGreg Kroah-Hartman 8938ffdff6aSGreg Kroah-Hartman /** 8948ffdff6aSGreg Kroah-Hartman * comedi_request_region() - Request an I/O region for a legacy driver 8958ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 8968ffdff6aSGreg Kroah-Hartman * @start: Base address of the I/O region. 8978ffdff6aSGreg Kroah-Hartman * @len: Length of the I/O region. 8988ffdff6aSGreg Kroah-Hartman * 8998ffdff6aSGreg Kroah-Hartman * Requests the specified I/O port region which must start at a non-zero 9008ffdff6aSGreg Kroah-Hartman * address. 9018ffdff6aSGreg Kroah-Hartman * 9028ffdff6aSGreg Kroah-Hartman * On success, @dev->iobase is set to the base address of the region and 9038ffdff6aSGreg Kroah-Hartman * @dev->iolen is set to its length. 9048ffdff6aSGreg Kroah-Hartman * 9058ffdff6aSGreg Kroah-Hartman * Returns 0 on success, -EINVAL if @start is 0, or -EIO if the request 9068ffdff6aSGreg Kroah-Hartman * fails. 9078ffdff6aSGreg Kroah-Hartman */ 9088ffdff6aSGreg Kroah-Hartman int comedi_request_region(struct comedi_device *dev, 9098ffdff6aSGreg Kroah-Hartman unsigned long start, unsigned long len) 9108ffdff6aSGreg Kroah-Hartman { 9118ffdff6aSGreg Kroah-Hartman int ret; 9128ffdff6aSGreg Kroah-Hartman 9138ffdff6aSGreg Kroah-Hartman ret = __comedi_request_region(dev, start, len); 9148ffdff6aSGreg Kroah-Hartman if (ret == 0) { 9158ffdff6aSGreg Kroah-Hartman dev->iobase = start; 9168ffdff6aSGreg Kroah-Hartman dev->iolen = len; 9178ffdff6aSGreg Kroah-Hartman } 9188ffdff6aSGreg Kroah-Hartman 9198ffdff6aSGreg Kroah-Hartman return ret; 9208ffdff6aSGreg Kroah-Hartman } 9218ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_request_region); 9228ffdff6aSGreg Kroah-Hartman 9238ffdff6aSGreg Kroah-Hartman /** 9248ffdff6aSGreg Kroah-Hartman * comedi_legacy_detach() - A generic (*detach) function for legacy drivers 9258ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 9268ffdff6aSGreg Kroah-Hartman * 9278ffdff6aSGreg Kroah-Hartman * This is a simple, generic 'detach' handler for legacy COMEDI devices that 9288ffdff6aSGreg Kroah-Hartman * just use a single I/O port region and possibly an IRQ and that don't need 9298ffdff6aSGreg Kroah-Hartman * any special clean-up for their private device or subdevice storage. It 9308ffdff6aSGreg Kroah-Hartman * can also be called by a driver-specific 'detach' handler. 9318ffdff6aSGreg Kroah-Hartman * 9328ffdff6aSGreg Kroah-Hartman * If @dev->irq is non-zero, the IRQ will be freed. If @dev->iobase and 9338ffdff6aSGreg Kroah-Hartman * @dev->iolen are both non-zero, the I/O port region will be released. 9348ffdff6aSGreg Kroah-Hartman */ 9358ffdff6aSGreg Kroah-Hartman void comedi_legacy_detach(struct comedi_device *dev) 9368ffdff6aSGreg Kroah-Hartman { 9378ffdff6aSGreg Kroah-Hartman if (dev->irq) { 9388ffdff6aSGreg Kroah-Hartman free_irq(dev->irq, dev); 9398ffdff6aSGreg Kroah-Hartman dev->irq = 0; 9408ffdff6aSGreg Kroah-Hartman } 9418ffdff6aSGreg Kroah-Hartman if (dev->iobase && dev->iolen) { 9428ffdff6aSGreg Kroah-Hartman release_region(dev->iobase, dev->iolen); 9438ffdff6aSGreg Kroah-Hartman dev->iobase = 0; 9448ffdff6aSGreg Kroah-Hartman dev->iolen = 0; 9458ffdff6aSGreg Kroah-Hartman } 9468ffdff6aSGreg Kroah-Hartman } 9478ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_legacy_detach); 9488ffdff6aSGreg Kroah-Hartman 9498ffdff6aSGreg Kroah-Hartman int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it) 9508ffdff6aSGreg Kroah-Hartman { 9518ffdff6aSGreg Kroah-Hartman struct comedi_driver *driv; 9528ffdff6aSGreg Kroah-Hartman int ret; 9538ffdff6aSGreg Kroah-Hartman 9548ffdff6aSGreg Kroah-Hartman lockdep_assert_held(&dev->mutex); 9558ffdff6aSGreg Kroah-Hartman if (dev->attached) 9568ffdff6aSGreg Kroah-Hartman return -EBUSY; 9578ffdff6aSGreg Kroah-Hartman 9588ffdff6aSGreg Kroah-Hartman mutex_lock(&comedi_drivers_list_lock); 9598ffdff6aSGreg Kroah-Hartman for (driv = comedi_drivers; driv; driv = driv->next) { 9608ffdff6aSGreg Kroah-Hartman if (!try_module_get(driv->module)) 9618ffdff6aSGreg Kroah-Hartman continue; 9628ffdff6aSGreg Kroah-Hartman if (driv->num_names) { 9638ffdff6aSGreg Kroah-Hartman dev->board_ptr = comedi_recognize(driv, it->board_name); 9648ffdff6aSGreg Kroah-Hartman if (dev->board_ptr) 9658ffdff6aSGreg Kroah-Hartman break; 9668ffdff6aSGreg Kroah-Hartman } else if (strcmp(driv->driver_name, it->board_name) == 0) { 9678ffdff6aSGreg Kroah-Hartman break; 9688ffdff6aSGreg Kroah-Hartman } 9698ffdff6aSGreg Kroah-Hartman module_put(driv->module); 9708ffdff6aSGreg Kroah-Hartman } 9718ffdff6aSGreg Kroah-Hartman if (!driv) { 9728ffdff6aSGreg Kroah-Hartman /* recognize has failed if we get here */ 9738ffdff6aSGreg Kroah-Hartman /* report valid board names before returning error */ 9748ffdff6aSGreg Kroah-Hartman for (driv = comedi_drivers; driv; driv = driv->next) { 9758ffdff6aSGreg Kroah-Hartman if (!try_module_get(driv->module)) 9768ffdff6aSGreg Kroah-Hartman continue; 9778ffdff6aSGreg Kroah-Hartman comedi_report_boards(driv); 9788ffdff6aSGreg Kroah-Hartman module_put(driv->module); 9798ffdff6aSGreg Kroah-Hartman } 9808ffdff6aSGreg Kroah-Hartman ret = -EIO; 9818ffdff6aSGreg Kroah-Hartman goto out; 9828ffdff6aSGreg Kroah-Hartman } 9838ffdff6aSGreg Kroah-Hartman if (!driv->attach) { 9848ffdff6aSGreg Kroah-Hartman /* driver does not support manual configuration */ 9858ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, 9868ffdff6aSGreg Kroah-Hartman "driver '%s' does not support attach using comedi_config\n", 9878ffdff6aSGreg Kroah-Hartman driv->driver_name); 9888ffdff6aSGreg Kroah-Hartman module_put(driv->module); 9898ffdff6aSGreg Kroah-Hartman ret = -EIO; 9908ffdff6aSGreg Kroah-Hartman goto out; 9918ffdff6aSGreg Kroah-Hartman } 9928ffdff6aSGreg Kroah-Hartman dev->driver = driv; 9938ffdff6aSGreg Kroah-Hartman dev->board_name = dev->board_ptr ? *(const char **)dev->board_ptr 9948ffdff6aSGreg Kroah-Hartman : dev->driver->driver_name; 9958ffdff6aSGreg Kroah-Hartman ret = driv->attach(dev, it); 9968ffdff6aSGreg Kroah-Hartman if (ret >= 0) 9978ffdff6aSGreg Kroah-Hartman ret = comedi_device_postconfig(dev); 9988ffdff6aSGreg Kroah-Hartman if (ret < 0) { 9998ffdff6aSGreg Kroah-Hartman comedi_device_detach(dev); 10008ffdff6aSGreg Kroah-Hartman module_put(driv->module); 10018ffdff6aSGreg Kroah-Hartman } 10028ffdff6aSGreg Kroah-Hartman /* On success, the driver module count has been incremented. */ 10038ffdff6aSGreg Kroah-Hartman out: 10048ffdff6aSGreg Kroah-Hartman mutex_unlock(&comedi_drivers_list_lock); 10058ffdff6aSGreg Kroah-Hartman return ret; 10068ffdff6aSGreg Kroah-Hartman } 10078ffdff6aSGreg Kroah-Hartman 10088ffdff6aSGreg Kroah-Hartman /** 10098ffdff6aSGreg Kroah-Hartman * comedi_auto_config() - Create a COMEDI device for a hardware device 10108ffdff6aSGreg Kroah-Hartman * @hardware_device: Hardware device. 10118ffdff6aSGreg Kroah-Hartman * @driver: COMEDI low-level driver for the hardware device. 10128ffdff6aSGreg Kroah-Hartman * @context: Driver context for the auto_attach handler. 10138ffdff6aSGreg Kroah-Hartman * 10148ffdff6aSGreg Kroah-Hartman * Allocates a new COMEDI device for the hardware device and calls the 10158ffdff6aSGreg Kroah-Hartman * low-level driver's 'auto_attach' handler to set-up the hardware and 10168ffdff6aSGreg Kroah-Hartman * allocate the COMEDI subdevices. Additional "post-configuration" setting 10178ffdff6aSGreg Kroah-Hartman * up is performed on successful return from the 'auto_attach' handler. 10188ffdff6aSGreg Kroah-Hartman * If the 'auto_attach' handler fails, the low-level driver's 'detach' 10198ffdff6aSGreg Kroah-Hartman * handler will be called as part of the clean-up. 10208ffdff6aSGreg Kroah-Hartman * 10218ffdff6aSGreg Kroah-Hartman * This is usually called from a wrapper function in a bus-specific COMEDI 10228ffdff6aSGreg Kroah-Hartman * module, which in turn is usually called from a bus device 'probe' 10238ffdff6aSGreg Kroah-Hartman * function in the low-level driver. 10248ffdff6aSGreg Kroah-Hartman * 10258ffdff6aSGreg Kroah-Hartman * Returns 0 on success, -EINVAL if the parameters are invalid or the 10268ffdff6aSGreg Kroah-Hartman * post-configuration determines the driver has set the COMEDI device up 10278ffdff6aSGreg Kroah-Hartman * incorrectly, -ENOMEM if failed to allocate memory, -EBUSY if run out of 10288ffdff6aSGreg Kroah-Hartman * COMEDI minor device numbers, or some negative error number returned by 10298ffdff6aSGreg Kroah-Hartman * the driver's 'auto_attach' handler. 10308ffdff6aSGreg Kroah-Hartman */ 10318ffdff6aSGreg Kroah-Hartman int comedi_auto_config(struct device *hardware_device, 10328ffdff6aSGreg Kroah-Hartman struct comedi_driver *driver, unsigned long context) 10338ffdff6aSGreg Kroah-Hartman { 10348ffdff6aSGreg Kroah-Hartman struct comedi_device *dev; 10358ffdff6aSGreg Kroah-Hartman int ret; 10368ffdff6aSGreg Kroah-Hartman 10378ffdff6aSGreg Kroah-Hartman if (!hardware_device) { 10388ffdff6aSGreg Kroah-Hartman pr_warn("BUG! %s called with NULL hardware_device\n", __func__); 10398ffdff6aSGreg Kroah-Hartman return -EINVAL; 10408ffdff6aSGreg Kroah-Hartman } 10418ffdff6aSGreg Kroah-Hartman if (!driver) { 10428ffdff6aSGreg Kroah-Hartman dev_warn(hardware_device, 10438ffdff6aSGreg Kroah-Hartman "BUG! %s called with NULL comedi driver\n", __func__); 10448ffdff6aSGreg Kroah-Hartman return -EINVAL; 10458ffdff6aSGreg Kroah-Hartman } 10468ffdff6aSGreg Kroah-Hartman 10478ffdff6aSGreg Kroah-Hartman if (!driver->auto_attach) { 10488ffdff6aSGreg Kroah-Hartman dev_warn(hardware_device, 10498ffdff6aSGreg Kroah-Hartman "BUG! comedi driver '%s' has no auto_attach handler\n", 10508ffdff6aSGreg Kroah-Hartman driver->driver_name); 10518ffdff6aSGreg Kroah-Hartman return -EINVAL; 10528ffdff6aSGreg Kroah-Hartman } 10538ffdff6aSGreg Kroah-Hartman 10548ffdff6aSGreg Kroah-Hartman dev = comedi_alloc_board_minor(hardware_device); 10558ffdff6aSGreg Kroah-Hartman if (IS_ERR(dev)) { 10568ffdff6aSGreg Kroah-Hartman dev_warn(hardware_device, 10578ffdff6aSGreg Kroah-Hartman "driver '%s' could not create device.\n", 10588ffdff6aSGreg Kroah-Hartman driver->driver_name); 10598ffdff6aSGreg Kroah-Hartman return PTR_ERR(dev); 10608ffdff6aSGreg Kroah-Hartman } 10618ffdff6aSGreg Kroah-Hartman /* Note: comedi_alloc_board_minor() locked dev->mutex. */ 10628ffdff6aSGreg Kroah-Hartman lockdep_assert_held(&dev->mutex); 10638ffdff6aSGreg Kroah-Hartman 10648ffdff6aSGreg Kroah-Hartman dev->driver = driver; 10658ffdff6aSGreg Kroah-Hartman dev->board_name = dev->driver->driver_name; 10668ffdff6aSGreg Kroah-Hartman ret = driver->auto_attach(dev, context); 10678ffdff6aSGreg Kroah-Hartman if (ret >= 0) 10688ffdff6aSGreg Kroah-Hartman ret = comedi_device_postconfig(dev); 10698ffdff6aSGreg Kroah-Hartman 10708ffdff6aSGreg Kroah-Hartman if (ret < 0) { 10718ffdff6aSGreg Kroah-Hartman dev_warn(hardware_device, 10728ffdff6aSGreg Kroah-Hartman "driver '%s' failed to auto-configure device.\n", 10738ffdff6aSGreg Kroah-Hartman driver->driver_name); 10748ffdff6aSGreg Kroah-Hartman mutex_unlock(&dev->mutex); 10758ffdff6aSGreg Kroah-Hartman comedi_release_hardware_device(hardware_device); 10768ffdff6aSGreg Kroah-Hartman } else { 10778ffdff6aSGreg Kroah-Hartman /* 10788ffdff6aSGreg Kroah-Hartman * class_dev should be set properly here 10798ffdff6aSGreg Kroah-Hartman * after a successful auto config 10808ffdff6aSGreg Kroah-Hartman */ 10818ffdff6aSGreg Kroah-Hartman dev_info(dev->class_dev, 10828ffdff6aSGreg Kroah-Hartman "driver '%s' has successfully auto-configured '%s'.\n", 10838ffdff6aSGreg Kroah-Hartman driver->driver_name, dev->board_name); 10848ffdff6aSGreg Kroah-Hartman mutex_unlock(&dev->mutex); 10858ffdff6aSGreg Kroah-Hartman } 10868ffdff6aSGreg Kroah-Hartman return ret; 10878ffdff6aSGreg Kroah-Hartman } 10888ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_auto_config); 10898ffdff6aSGreg Kroah-Hartman 10908ffdff6aSGreg Kroah-Hartman /** 10918ffdff6aSGreg Kroah-Hartman * comedi_auto_unconfig() - Unconfigure auto-allocated COMEDI device 10928ffdff6aSGreg Kroah-Hartman * @hardware_device: Hardware device previously passed to 10938ffdff6aSGreg Kroah-Hartman * comedi_auto_config(). 10948ffdff6aSGreg Kroah-Hartman * 10958ffdff6aSGreg Kroah-Hartman * Cleans up and eventually destroys the COMEDI device allocated by 10968ffdff6aSGreg Kroah-Hartman * comedi_auto_config() for the same hardware device. As part of this 10978ffdff6aSGreg Kroah-Hartman * clean-up, the low-level COMEDI driver's 'detach' handler will be called. 10988ffdff6aSGreg Kroah-Hartman * (The COMEDI device itself will persist in an unattached state if it is 10998ffdff6aSGreg Kroah-Hartman * still open, until it is released, and any mmapped buffers will persist 11008ffdff6aSGreg Kroah-Hartman * until they are munmapped.) 11018ffdff6aSGreg Kroah-Hartman * 11028ffdff6aSGreg Kroah-Hartman * This is usually called from a wrapper module in a bus-specific COMEDI 11038ffdff6aSGreg Kroah-Hartman * module, which in turn is usually set as the bus device 'remove' function 11048ffdff6aSGreg Kroah-Hartman * in the low-level COMEDI driver. 11058ffdff6aSGreg Kroah-Hartman */ 11068ffdff6aSGreg Kroah-Hartman void comedi_auto_unconfig(struct device *hardware_device) 11078ffdff6aSGreg Kroah-Hartman { 11088ffdff6aSGreg Kroah-Hartman if (!hardware_device) 11098ffdff6aSGreg Kroah-Hartman return; 11108ffdff6aSGreg Kroah-Hartman comedi_release_hardware_device(hardware_device); 11118ffdff6aSGreg Kroah-Hartman } 11128ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_auto_unconfig); 11138ffdff6aSGreg Kroah-Hartman 11148ffdff6aSGreg Kroah-Hartman /** 11158ffdff6aSGreg Kroah-Hartman * comedi_driver_register() - Register a low-level COMEDI driver 11168ffdff6aSGreg Kroah-Hartman * @driver: Low-level COMEDI driver. 11178ffdff6aSGreg Kroah-Hartman * 11188ffdff6aSGreg Kroah-Hartman * The low-level COMEDI driver is added to the list of registered COMEDI 11198ffdff6aSGreg Kroah-Hartman * drivers. This is used by the handler for the "/proc/comedi" file and is 11208ffdff6aSGreg Kroah-Hartman * also used by the handler for the %COMEDI_DEVCONFIG ioctl to configure 11218ffdff6aSGreg Kroah-Hartman * "legacy" COMEDI devices (for those low-level drivers that support it). 11228ffdff6aSGreg Kroah-Hartman * 11238ffdff6aSGreg Kroah-Hartman * Returns 0. 11248ffdff6aSGreg Kroah-Hartman */ 11258ffdff6aSGreg Kroah-Hartman int comedi_driver_register(struct comedi_driver *driver) 11268ffdff6aSGreg Kroah-Hartman { 11278ffdff6aSGreg Kroah-Hartman mutex_lock(&comedi_drivers_list_lock); 11288ffdff6aSGreg Kroah-Hartman driver->next = comedi_drivers; 11298ffdff6aSGreg Kroah-Hartman comedi_drivers = driver; 11308ffdff6aSGreg Kroah-Hartman mutex_unlock(&comedi_drivers_list_lock); 11318ffdff6aSGreg Kroah-Hartman 11328ffdff6aSGreg Kroah-Hartman return 0; 11338ffdff6aSGreg Kroah-Hartman } 11348ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_driver_register); 11358ffdff6aSGreg Kroah-Hartman 11368ffdff6aSGreg Kroah-Hartman /** 11378ffdff6aSGreg Kroah-Hartman * comedi_driver_unregister() - Unregister a low-level COMEDI driver 11388ffdff6aSGreg Kroah-Hartman * @driver: Low-level COMEDI driver. 11398ffdff6aSGreg Kroah-Hartman * 11408ffdff6aSGreg Kroah-Hartman * The low-level COMEDI driver is removed from the list of registered COMEDI 11418ffdff6aSGreg Kroah-Hartman * drivers. Detaches any COMEDI devices attached to the driver, which will 11428ffdff6aSGreg Kroah-Hartman * result in the low-level driver's 'detach' handler being called for those 11438ffdff6aSGreg Kroah-Hartman * devices before this function returns. 11448ffdff6aSGreg Kroah-Hartman */ 11458ffdff6aSGreg Kroah-Hartman void comedi_driver_unregister(struct comedi_driver *driver) 11468ffdff6aSGreg Kroah-Hartman { 11478ffdff6aSGreg Kroah-Hartman struct comedi_driver *prev; 11488ffdff6aSGreg Kroah-Hartman int i; 11498ffdff6aSGreg Kroah-Hartman 11508ffdff6aSGreg Kroah-Hartman /* unlink the driver */ 11518ffdff6aSGreg Kroah-Hartman mutex_lock(&comedi_drivers_list_lock); 11528ffdff6aSGreg Kroah-Hartman if (comedi_drivers == driver) { 11538ffdff6aSGreg Kroah-Hartman comedi_drivers = driver->next; 11548ffdff6aSGreg Kroah-Hartman } else { 11558ffdff6aSGreg Kroah-Hartman for (prev = comedi_drivers; prev->next; prev = prev->next) { 11568ffdff6aSGreg Kroah-Hartman if (prev->next == driver) { 11578ffdff6aSGreg Kroah-Hartman prev->next = driver->next; 11588ffdff6aSGreg Kroah-Hartman break; 11598ffdff6aSGreg Kroah-Hartman } 11608ffdff6aSGreg Kroah-Hartman } 11618ffdff6aSGreg Kroah-Hartman } 11628ffdff6aSGreg Kroah-Hartman mutex_unlock(&comedi_drivers_list_lock); 11638ffdff6aSGreg Kroah-Hartman 11648ffdff6aSGreg Kroah-Hartman /* check for devices using this driver */ 11658ffdff6aSGreg Kroah-Hartman for (i = 0; i < COMEDI_NUM_BOARD_MINORS; i++) { 11668ffdff6aSGreg Kroah-Hartman struct comedi_device *dev = comedi_dev_get_from_minor(i); 11678ffdff6aSGreg Kroah-Hartman 11688ffdff6aSGreg Kroah-Hartman if (!dev) 11698ffdff6aSGreg Kroah-Hartman continue; 11708ffdff6aSGreg Kroah-Hartman 11718ffdff6aSGreg Kroah-Hartman mutex_lock(&dev->mutex); 11728ffdff6aSGreg Kroah-Hartman if (dev->attached && dev->driver == driver) { 11738ffdff6aSGreg Kroah-Hartman if (dev->use_count) 11748ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, 11758ffdff6aSGreg Kroah-Hartman "BUG! detaching device with use_count=%d\n", 11768ffdff6aSGreg Kroah-Hartman dev->use_count); 11778ffdff6aSGreg Kroah-Hartman comedi_device_detach(dev); 11788ffdff6aSGreg Kroah-Hartman } 11798ffdff6aSGreg Kroah-Hartman mutex_unlock(&dev->mutex); 11808ffdff6aSGreg Kroah-Hartman comedi_dev_put(dev); 11818ffdff6aSGreg Kroah-Hartman } 11828ffdff6aSGreg Kroah-Hartman } 11838ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_driver_unregister); 1184