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