1*8ffdff6aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+ 2*8ffdff6aSGreg Kroah-Hartman /* 3*8ffdff6aSGreg Kroah-Hartman * module/drivers.c 4*8ffdff6aSGreg Kroah-Hartman * functions for manipulating drivers 5*8ffdff6aSGreg Kroah-Hartman * 6*8ffdff6aSGreg Kroah-Hartman * COMEDI - Linux Control and Measurement Device Interface 7*8ffdff6aSGreg Kroah-Hartman * Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org> 8*8ffdff6aSGreg Kroah-Hartman * Copyright (C) 2002 Frank Mori Hess <fmhess@users.sourceforge.net> 9*8ffdff6aSGreg Kroah-Hartman */ 10*8ffdff6aSGreg Kroah-Hartman 11*8ffdff6aSGreg Kroah-Hartman #include <linux/device.h> 12*8ffdff6aSGreg Kroah-Hartman #include <linux/module.h> 13*8ffdff6aSGreg Kroah-Hartman #include <linux/errno.h> 14*8ffdff6aSGreg Kroah-Hartman #include <linux/kernel.h> 15*8ffdff6aSGreg Kroah-Hartman #include <linux/ioport.h> 16*8ffdff6aSGreg Kroah-Hartman #include <linux/slab.h> 17*8ffdff6aSGreg Kroah-Hartman #include <linux/dma-direction.h> 18*8ffdff6aSGreg Kroah-Hartman #include <linux/interrupt.h> 19*8ffdff6aSGreg Kroah-Hartman #include <linux/firmware.h> 20*8ffdff6aSGreg Kroah-Hartman 21*8ffdff6aSGreg Kroah-Hartman #include "comedidev.h" 22*8ffdff6aSGreg Kroah-Hartman #include "comedi_internal.h" 23*8ffdff6aSGreg Kroah-Hartman 24*8ffdff6aSGreg Kroah-Hartman struct comedi_driver *comedi_drivers; 25*8ffdff6aSGreg Kroah-Hartman /* protects access to comedi_drivers */ 26*8ffdff6aSGreg Kroah-Hartman DEFINE_MUTEX(comedi_drivers_list_lock); 27*8ffdff6aSGreg Kroah-Hartman 28*8ffdff6aSGreg Kroah-Hartman /** 29*8ffdff6aSGreg Kroah-Hartman * comedi_set_hw_dev() - Set hardware device associated with COMEDI device 30*8ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 31*8ffdff6aSGreg Kroah-Hartman * @hw_dev: Hardware device. 32*8ffdff6aSGreg Kroah-Hartman * 33*8ffdff6aSGreg Kroah-Hartman * For automatically configured COMEDI devices (resulting from a call to 34*8ffdff6aSGreg Kroah-Hartman * comedi_auto_config() or one of its wrappers from the low-level COMEDI 35*8ffdff6aSGreg Kroah-Hartman * driver), comedi_set_hw_dev() is called automatically by the COMEDI core 36*8ffdff6aSGreg Kroah-Hartman * to associate the COMEDI device with the hardware device. It can also be 37*8ffdff6aSGreg Kroah-Hartman * called directly by "legacy" low-level COMEDI drivers that rely on the 38*8ffdff6aSGreg Kroah-Hartman * %COMEDI_DEVCONFIG ioctl to configure the hardware as long as the hardware 39*8ffdff6aSGreg Kroah-Hartman * has a &struct device. 40*8ffdff6aSGreg Kroah-Hartman * 41*8ffdff6aSGreg Kroah-Hartman * If @dev->hw_dev is NULL, it gets a reference to @hw_dev and sets 42*8ffdff6aSGreg Kroah-Hartman * @dev->hw_dev, otherwise, it does nothing. Calling it multiple times 43*8ffdff6aSGreg Kroah-Hartman * with the same hardware device is not considered an error. If it gets 44*8ffdff6aSGreg Kroah-Hartman * a reference to the hardware device, it will be automatically 'put' when 45*8ffdff6aSGreg Kroah-Hartman * the device is detached from COMEDI. 46*8ffdff6aSGreg Kroah-Hartman * 47*8ffdff6aSGreg Kroah-Hartman * Returns 0 if @dev->hw_dev was NULL or the same as @hw_dev, otherwise 48*8ffdff6aSGreg Kroah-Hartman * returns -EEXIST. 49*8ffdff6aSGreg Kroah-Hartman */ 50*8ffdff6aSGreg Kroah-Hartman int comedi_set_hw_dev(struct comedi_device *dev, struct device *hw_dev) 51*8ffdff6aSGreg Kroah-Hartman { 52*8ffdff6aSGreg Kroah-Hartman if (hw_dev == dev->hw_dev) 53*8ffdff6aSGreg Kroah-Hartman return 0; 54*8ffdff6aSGreg Kroah-Hartman if (dev->hw_dev) 55*8ffdff6aSGreg Kroah-Hartman return -EEXIST; 56*8ffdff6aSGreg Kroah-Hartman dev->hw_dev = get_device(hw_dev); 57*8ffdff6aSGreg Kroah-Hartman return 0; 58*8ffdff6aSGreg Kroah-Hartman } 59*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_set_hw_dev); 60*8ffdff6aSGreg Kroah-Hartman 61*8ffdff6aSGreg Kroah-Hartman static void comedi_clear_hw_dev(struct comedi_device *dev) 62*8ffdff6aSGreg Kroah-Hartman { 63*8ffdff6aSGreg Kroah-Hartman put_device(dev->hw_dev); 64*8ffdff6aSGreg Kroah-Hartman dev->hw_dev = NULL; 65*8ffdff6aSGreg Kroah-Hartman } 66*8ffdff6aSGreg Kroah-Hartman 67*8ffdff6aSGreg Kroah-Hartman /** 68*8ffdff6aSGreg Kroah-Hartman * comedi_alloc_devpriv() - Allocate memory for the device private data 69*8ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 70*8ffdff6aSGreg Kroah-Hartman * @size: Size of the memory to allocate. 71*8ffdff6aSGreg Kroah-Hartman * 72*8ffdff6aSGreg Kroah-Hartman * The allocated memory is zero-filled. @dev->private points to it on 73*8ffdff6aSGreg Kroah-Hartman * return. The memory will be automatically freed when the COMEDI device is 74*8ffdff6aSGreg Kroah-Hartman * "detached". 75*8ffdff6aSGreg Kroah-Hartman * 76*8ffdff6aSGreg Kroah-Hartman * Returns a pointer to the allocated memory, or NULL on failure. 77*8ffdff6aSGreg Kroah-Hartman */ 78*8ffdff6aSGreg Kroah-Hartman void *comedi_alloc_devpriv(struct comedi_device *dev, size_t size) 79*8ffdff6aSGreg Kroah-Hartman { 80*8ffdff6aSGreg Kroah-Hartman dev->private = kzalloc(size, GFP_KERNEL); 81*8ffdff6aSGreg Kroah-Hartman return dev->private; 82*8ffdff6aSGreg Kroah-Hartman } 83*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_alloc_devpriv); 84*8ffdff6aSGreg Kroah-Hartman 85*8ffdff6aSGreg Kroah-Hartman /** 86*8ffdff6aSGreg Kroah-Hartman * comedi_alloc_subdevices() - Allocate subdevices for COMEDI device 87*8ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 88*8ffdff6aSGreg Kroah-Hartman * @num_subdevices: Number of subdevices to allocate. 89*8ffdff6aSGreg Kroah-Hartman * 90*8ffdff6aSGreg Kroah-Hartman * Allocates and initializes an array of &struct comedi_subdevice for the 91*8ffdff6aSGreg Kroah-Hartman * COMEDI device. If successful, sets @dev->subdevices to point to the 92*8ffdff6aSGreg Kroah-Hartman * first one and @dev->n_subdevices to the number. 93*8ffdff6aSGreg Kroah-Hartman * 94*8ffdff6aSGreg Kroah-Hartman * Returns 0 on success, -EINVAL if @num_subdevices is < 1, or -ENOMEM if 95*8ffdff6aSGreg Kroah-Hartman * failed to allocate the memory. 96*8ffdff6aSGreg Kroah-Hartman */ 97*8ffdff6aSGreg Kroah-Hartman int comedi_alloc_subdevices(struct comedi_device *dev, int num_subdevices) 98*8ffdff6aSGreg Kroah-Hartman { 99*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s; 100*8ffdff6aSGreg Kroah-Hartman int i; 101*8ffdff6aSGreg Kroah-Hartman 102*8ffdff6aSGreg Kroah-Hartman if (num_subdevices < 1) 103*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 104*8ffdff6aSGreg Kroah-Hartman 105*8ffdff6aSGreg Kroah-Hartman s = kcalloc(num_subdevices, sizeof(*s), GFP_KERNEL); 106*8ffdff6aSGreg Kroah-Hartman if (!s) 107*8ffdff6aSGreg Kroah-Hartman return -ENOMEM; 108*8ffdff6aSGreg Kroah-Hartman dev->subdevices = s; 109*8ffdff6aSGreg Kroah-Hartman dev->n_subdevices = num_subdevices; 110*8ffdff6aSGreg Kroah-Hartman 111*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < num_subdevices; ++i) { 112*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[i]; 113*8ffdff6aSGreg Kroah-Hartman s->device = dev; 114*8ffdff6aSGreg Kroah-Hartman s->index = i; 115*8ffdff6aSGreg Kroah-Hartman s->async_dma_dir = DMA_NONE; 116*8ffdff6aSGreg Kroah-Hartman spin_lock_init(&s->spin_lock); 117*8ffdff6aSGreg Kroah-Hartman s->minor = -1; 118*8ffdff6aSGreg Kroah-Hartman } 119*8ffdff6aSGreg Kroah-Hartman return 0; 120*8ffdff6aSGreg Kroah-Hartman } 121*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_alloc_subdevices); 122*8ffdff6aSGreg Kroah-Hartman 123*8ffdff6aSGreg Kroah-Hartman /** 124*8ffdff6aSGreg Kroah-Hartman * comedi_alloc_subdev_readback() - Allocate memory for the subdevice readback 125*8ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 126*8ffdff6aSGreg Kroah-Hartman * 127*8ffdff6aSGreg Kroah-Hartman * This is called by low-level COMEDI drivers to allocate an array to record 128*8ffdff6aSGreg Kroah-Hartman * the last values written to a subdevice's analog output channels (at least 129*8ffdff6aSGreg Kroah-Hartman * by the %INSN_WRITE instruction), to allow them to be read back by an 130*8ffdff6aSGreg Kroah-Hartman * %INSN_READ instruction. It also provides a default handler for the 131*8ffdff6aSGreg Kroah-Hartman * %INSN_READ instruction unless one has already been set. 132*8ffdff6aSGreg Kroah-Hartman * 133*8ffdff6aSGreg Kroah-Hartman * On success, @s->readback points to the first element of the array, which 134*8ffdff6aSGreg Kroah-Hartman * is zero-filled. The low-level driver is responsible for updating its 135*8ffdff6aSGreg Kroah-Hartman * contents. @s->insn_read will be set to comedi_readback_insn_read() 136*8ffdff6aSGreg Kroah-Hartman * unless it is already non-NULL. 137*8ffdff6aSGreg Kroah-Hartman * 138*8ffdff6aSGreg Kroah-Hartman * Returns 0 on success, -EINVAL if the subdevice has no channels, or 139*8ffdff6aSGreg Kroah-Hartman * -ENOMEM on allocation failure. 140*8ffdff6aSGreg Kroah-Hartman */ 141*8ffdff6aSGreg Kroah-Hartman int comedi_alloc_subdev_readback(struct comedi_subdevice *s) 142*8ffdff6aSGreg Kroah-Hartman { 143*8ffdff6aSGreg Kroah-Hartman if (!s->n_chan) 144*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 145*8ffdff6aSGreg Kroah-Hartman 146*8ffdff6aSGreg Kroah-Hartman s->readback = kcalloc(s->n_chan, sizeof(*s->readback), GFP_KERNEL); 147*8ffdff6aSGreg Kroah-Hartman if (!s->readback) 148*8ffdff6aSGreg Kroah-Hartman return -ENOMEM; 149*8ffdff6aSGreg Kroah-Hartman 150*8ffdff6aSGreg Kroah-Hartman if (!s->insn_read) 151*8ffdff6aSGreg Kroah-Hartman s->insn_read = comedi_readback_insn_read; 152*8ffdff6aSGreg Kroah-Hartman 153*8ffdff6aSGreg Kroah-Hartman return 0; 154*8ffdff6aSGreg Kroah-Hartman } 155*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_alloc_subdev_readback); 156*8ffdff6aSGreg Kroah-Hartman 157*8ffdff6aSGreg Kroah-Hartman static void comedi_device_detach_cleanup(struct comedi_device *dev) 158*8ffdff6aSGreg Kroah-Hartman { 159*8ffdff6aSGreg Kroah-Hartman int i; 160*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s; 161*8ffdff6aSGreg Kroah-Hartman 162*8ffdff6aSGreg Kroah-Hartman lockdep_assert_held(&dev->attach_lock); 163*8ffdff6aSGreg Kroah-Hartman lockdep_assert_held(&dev->mutex); 164*8ffdff6aSGreg Kroah-Hartman if (dev->subdevices) { 165*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < dev->n_subdevices; i++) { 166*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[i]; 167*8ffdff6aSGreg Kroah-Hartman if (comedi_can_auto_free_spriv(s)) 168*8ffdff6aSGreg Kroah-Hartman kfree(s->private); 169*8ffdff6aSGreg Kroah-Hartman comedi_free_subdevice_minor(s); 170*8ffdff6aSGreg Kroah-Hartman if (s->async) { 171*8ffdff6aSGreg Kroah-Hartman comedi_buf_alloc(dev, s, 0); 172*8ffdff6aSGreg Kroah-Hartman kfree(s->async); 173*8ffdff6aSGreg Kroah-Hartman } 174*8ffdff6aSGreg Kroah-Hartman kfree(s->readback); 175*8ffdff6aSGreg Kroah-Hartman } 176*8ffdff6aSGreg Kroah-Hartman kfree(dev->subdevices); 177*8ffdff6aSGreg Kroah-Hartman dev->subdevices = NULL; 178*8ffdff6aSGreg Kroah-Hartman dev->n_subdevices = 0; 179*8ffdff6aSGreg Kroah-Hartman } 180*8ffdff6aSGreg Kroah-Hartman kfree(dev->private); 181*8ffdff6aSGreg Kroah-Hartman kfree(dev->pacer); 182*8ffdff6aSGreg Kroah-Hartman dev->private = NULL; 183*8ffdff6aSGreg Kroah-Hartman dev->pacer = NULL; 184*8ffdff6aSGreg Kroah-Hartman dev->driver = NULL; 185*8ffdff6aSGreg Kroah-Hartman dev->board_name = NULL; 186*8ffdff6aSGreg Kroah-Hartman dev->board_ptr = NULL; 187*8ffdff6aSGreg Kroah-Hartman dev->mmio = NULL; 188*8ffdff6aSGreg Kroah-Hartman dev->iobase = 0; 189*8ffdff6aSGreg Kroah-Hartman dev->iolen = 0; 190*8ffdff6aSGreg Kroah-Hartman dev->ioenabled = false; 191*8ffdff6aSGreg Kroah-Hartman dev->irq = 0; 192*8ffdff6aSGreg Kroah-Hartman dev->read_subdev = NULL; 193*8ffdff6aSGreg Kroah-Hartman dev->write_subdev = NULL; 194*8ffdff6aSGreg Kroah-Hartman dev->open = NULL; 195*8ffdff6aSGreg Kroah-Hartman dev->close = NULL; 196*8ffdff6aSGreg Kroah-Hartman comedi_clear_hw_dev(dev); 197*8ffdff6aSGreg Kroah-Hartman } 198*8ffdff6aSGreg Kroah-Hartman 199*8ffdff6aSGreg Kroah-Hartman void comedi_device_detach(struct comedi_device *dev) 200*8ffdff6aSGreg Kroah-Hartman { 201*8ffdff6aSGreg Kroah-Hartman lockdep_assert_held(&dev->mutex); 202*8ffdff6aSGreg Kroah-Hartman comedi_device_cancel_all(dev); 203*8ffdff6aSGreg Kroah-Hartman down_write(&dev->attach_lock); 204*8ffdff6aSGreg Kroah-Hartman dev->attached = false; 205*8ffdff6aSGreg Kroah-Hartman dev->detach_count++; 206*8ffdff6aSGreg Kroah-Hartman if (dev->driver) 207*8ffdff6aSGreg Kroah-Hartman dev->driver->detach(dev); 208*8ffdff6aSGreg Kroah-Hartman comedi_device_detach_cleanup(dev); 209*8ffdff6aSGreg Kroah-Hartman up_write(&dev->attach_lock); 210*8ffdff6aSGreg Kroah-Hartman } 211*8ffdff6aSGreg Kroah-Hartman 212*8ffdff6aSGreg Kroah-Hartman static int poll_invalid(struct comedi_device *dev, struct comedi_subdevice *s) 213*8ffdff6aSGreg Kroah-Hartman { 214*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 215*8ffdff6aSGreg Kroah-Hartman } 216*8ffdff6aSGreg Kroah-Hartman 217*8ffdff6aSGreg Kroah-Hartman static int insn_device_inval(struct comedi_device *dev, 218*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, unsigned int *data) 219*8ffdff6aSGreg Kroah-Hartman { 220*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 221*8ffdff6aSGreg Kroah-Hartman } 222*8ffdff6aSGreg Kroah-Hartman 223*8ffdff6aSGreg Kroah-Hartman static unsigned int get_zero_valid_routes(struct comedi_device *dev, 224*8ffdff6aSGreg Kroah-Hartman unsigned int n_pairs, 225*8ffdff6aSGreg Kroah-Hartman unsigned int *pair_data) 226*8ffdff6aSGreg Kroah-Hartman { 227*8ffdff6aSGreg Kroah-Hartman return 0; 228*8ffdff6aSGreg Kroah-Hartman } 229*8ffdff6aSGreg Kroah-Hartman 230*8ffdff6aSGreg Kroah-Hartman int insn_inval(struct comedi_device *dev, struct comedi_subdevice *s, 231*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, unsigned int *data) 232*8ffdff6aSGreg Kroah-Hartman { 233*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 234*8ffdff6aSGreg Kroah-Hartman } 235*8ffdff6aSGreg Kroah-Hartman 236*8ffdff6aSGreg Kroah-Hartman /** 237*8ffdff6aSGreg Kroah-Hartman * comedi_readback_insn_read() - A generic (*insn_read) for subdevice readback. 238*8ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 239*8ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 240*8ffdff6aSGreg Kroah-Hartman * @insn: COMEDI instruction. 241*8ffdff6aSGreg Kroah-Hartman * @data: Pointer to return the readback data. 242*8ffdff6aSGreg Kroah-Hartman * 243*8ffdff6aSGreg Kroah-Hartman * Handles the %INSN_READ instruction for subdevices that use the readback 244*8ffdff6aSGreg Kroah-Hartman * array allocated by comedi_alloc_subdev_readback(). It may be used 245*8ffdff6aSGreg Kroah-Hartman * directly as the subdevice's handler (@s->insn_read) or called via a 246*8ffdff6aSGreg Kroah-Hartman * wrapper. 247*8ffdff6aSGreg Kroah-Hartman * 248*8ffdff6aSGreg Kroah-Hartman * @insn->n is normally 1, which will read a single value. If higher, the 249*8ffdff6aSGreg Kroah-Hartman * same element of the readback array will be read multiple times. 250*8ffdff6aSGreg Kroah-Hartman * 251*8ffdff6aSGreg Kroah-Hartman * Returns @insn->n on success, or -EINVAL if @s->readback is NULL. 252*8ffdff6aSGreg Kroah-Hartman */ 253*8ffdff6aSGreg Kroah-Hartman int comedi_readback_insn_read(struct comedi_device *dev, 254*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 255*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 256*8ffdff6aSGreg Kroah-Hartman unsigned int *data) 257*8ffdff6aSGreg Kroah-Hartman { 258*8ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec); 259*8ffdff6aSGreg Kroah-Hartman int i; 260*8ffdff6aSGreg Kroah-Hartman 261*8ffdff6aSGreg Kroah-Hartman if (!s->readback) 262*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 263*8ffdff6aSGreg Kroah-Hartman 264*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < insn->n; i++) 265*8ffdff6aSGreg Kroah-Hartman data[i] = s->readback[chan]; 266*8ffdff6aSGreg Kroah-Hartman 267*8ffdff6aSGreg Kroah-Hartman return insn->n; 268*8ffdff6aSGreg Kroah-Hartman } 269*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_readback_insn_read); 270*8ffdff6aSGreg Kroah-Hartman 271*8ffdff6aSGreg Kroah-Hartman /** 272*8ffdff6aSGreg Kroah-Hartman * comedi_timeout() - Busy-wait for a driver condition to occur 273*8ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 274*8ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 275*8ffdff6aSGreg Kroah-Hartman * @insn: COMEDI instruction. 276*8ffdff6aSGreg Kroah-Hartman * @cb: Callback to check for the condition. 277*8ffdff6aSGreg Kroah-Hartman * @context: Private context from the driver. 278*8ffdff6aSGreg Kroah-Hartman * 279*8ffdff6aSGreg Kroah-Hartman * Busy-waits for up to a second (%COMEDI_TIMEOUT_MS) for the condition or 280*8ffdff6aSGreg Kroah-Hartman * some error (other than -EBUSY) to occur. The parameters @dev, @s, @insn, 281*8ffdff6aSGreg Kroah-Hartman * and @context are passed to the callback function, which returns -EBUSY to 282*8ffdff6aSGreg Kroah-Hartman * continue waiting or some other value to stop waiting (generally 0 if the 283*8ffdff6aSGreg Kroah-Hartman * condition occurred, or some error value). 284*8ffdff6aSGreg Kroah-Hartman * 285*8ffdff6aSGreg Kroah-Hartman * Returns -ETIMEDOUT if timed out, otherwise the return value from the 286*8ffdff6aSGreg Kroah-Hartman * callback function. 287*8ffdff6aSGreg Kroah-Hartman */ 288*8ffdff6aSGreg Kroah-Hartman int comedi_timeout(struct comedi_device *dev, 289*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 290*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 291*8ffdff6aSGreg Kroah-Hartman int (*cb)(struct comedi_device *dev, 292*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 293*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 294*8ffdff6aSGreg Kroah-Hartman unsigned long context), 295*8ffdff6aSGreg Kroah-Hartman unsigned long context) 296*8ffdff6aSGreg Kroah-Hartman { 297*8ffdff6aSGreg Kroah-Hartman unsigned long timeout = jiffies + msecs_to_jiffies(COMEDI_TIMEOUT_MS); 298*8ffdff6aSGreg Kroah-Hartman int ret; 299*8ffdff6aSGreg Kroah-Hartman 300*8ffdff6aSGreg Kroah-Hartman while (time_before(jiffies, timeout)) { 301*8ffdff6aSGreg Kroah-Hartman ret = cb(dev, s, insn, context); 302*8ffdff6aSGreg Kroah-Hartman if (ret != -EBUSY) 303*8ffdff6aSGreg Kroah-Hartman return ret; /* success (0) or non EBUSY errno */ 304*8ffdff6aSGreg Kroah-Hartman cpu_relax(); 305*8ffdff6aSGreg Kroah-Hartman } 306*8ffdff6aSGreg Kroah-Hartman return -ETIMEDOUT; 307*8ffdff6aSGreg Kroah-Hartman } 308*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_timeout); 309*8ffdff6aSGreg Kroah-Hartman 310*8ffdff6aSGreg Kroah-Hartman /** 311*8ffdff6aSGreg Kroah-Hartman * comedi_dio_insn_config() - Boilerplate (*insn_config) for DIO subdevices 312*8ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 313*8ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 314*8ffdff6aSGreg Kroah-Hartman * @insn: COMEDI instruction. 315*8ffdff6aSGreg Kroah-Hartman * @data: Instruction parameters and return data. 316*8ffdff6aSGreg Kroah-Hartman * @mask: io_bits mask for grouped channels, or 0 for single channel. 317*8ffdff6aSGreg Kroah-Hartman * 318*8ffdff6aSGreg Kroah-Hartman * If @mask is 0, it is replaced with a single-bit mask corresponding to the 319*8ffdff6aSGreg Kroah-Hartman * channel number specified by @insn->chanspec. Otherwise, @mask 320*8ffdff6aSGreg Kroah-Hartman * corresponds to a group of channels (which should include the specified 321*8ffdff6aSGreg Kroah-Hartman * channel) that are always configured together as inputs or outputs. 322*8ffdff6aSGreg Kroah-Hartman * 323*8ffdff6aSGreg Kroah-Hartman * Partially handles the %INSN_CONFIG_DIO_INPUT, %INSN_CONFIG_DIO_OUTPUTS, 324*8ffdff6aSGreg Kroah-Hartman * and %INSN_CONFIG_DIO_QUERY instructions. The first two update 325*8ffdff6aSGreg Kroah-Hartman * @s->io_bits to record the directions of the masked channels. The last 326*8ffdff6aSGreg Kroah-Hartman * one sets @data[1] to the current direction of the group of channels 327*8ffdff6aSGreg Kroah-Hartman * (%COMEDI_INPUT) or %COMEDI_OUTPUT) as recorded in @s->io_bits. 328*8ffdff6aSGreg Kroah-Hartman * 329*8ffdff6aSGreg Kroah-Hartman * The caller is responsible for updating the DIO direction in the hardware 330*8ffdff6aSGreg Kroah-Hartman * registers if this function returns 0. 331*8ffdff6aSGreg Kroah-Hartman * 332*8ffdff6aSGreg Kroah-Hartman * Returns 0 for a %INSN_CONFIG_DIO_INPUT or %INSN_CONFIG_DIO_OUTPUT 333*8ffdff6aSGreg Kroah-Hartman * instruction, @insn->n (> 0) for a %INSN_CONFIG_DIO_QUERY instruction, or 334*8ffdff6aSGreg Kroah-Hartman * -EINVAL for some other instruction. 335*8ffdff6aSGreg Kroah-Hartman */ 336*8ffdff6aSGreg Kroah-Hartman int comedi_dio_insn_config(struct comedi_device *dev, 337*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 338*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 339*8ffdff6aSGreg Kroah-Hartman unsigned int *data, 340*8ffdff6aSGreg Kroah-Hartman unsigned int mask) 341*8ffdff6aSGreg Kroah-Hartman { 342*8ffdff6aSGreg Kroah-Hartman unsigned int chan_mask = 1 << CR_CHAN(insn->chanspec); 343*8ffdff6aSGreg Kroah-Hartman 344*8ffdff6aSGreg Kroah-Hartman if (!mask) 345*8ffdff6aSGreg Kroah-Hartman mask = chan_mask; 346*8ffdff6aSGreg Kroah-Hartman 347*8ffdff6aSGreg Kroah-Hartman switch (data[0]) { 348*8ffdff6aSGreg Kroah-Hartman case INSN_CONFIG_DIO_INPUT: 349*8ffdff6aSGreg Kroah-Hartman s->io_bits &= ~mask; 350*8ffdff6aSGreg Kroah-Hartman break; 351*8ffdff6aSGreg Kroah-Hartman 352*8ffdff6aSGreg Kroah-Hartman case INSN_CONFIG_DIO_OUTPUT: 353*8ffdff6aSGreg Kroah-Hartman s->io_bits |= mask; 354*8ffdff6aSGreg Kroah-Hartman break; 355*8ffdff6aSGreg Kroah-Hartman 356*8ffdff6aSGreg Kroah-Hartman case INSN_CONFIG_DIO_QUERY: 357*8ffdff6aSGreg Kroah-Hartman data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT; 358*8ffdff6aSGreg Kroah-Hartman return insn->n; 359*8ffdff6aSGreg Kroah-Hartman 360*8ffdff6aSGreg Kroah-Hartman default: 361*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 362*8ffdff6aSGreg Kroah-Hartman } 363*8ffdff6aSGreg Kroah-Hartman 364*8ffdff6aSGreg Kroah-Hartman return 0; 365*8ffdff6aSGreg Kroah-Hartman } 366*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_dio_insn_config); 367*8ffdff6aSGreg Kroah-Hartman 368*8ffdff6aSGreg Kroah-Hartman /** 369*8ffdff6aSGreg Kroah-Hartman * comedi_dio_update_state() - Update the internal state of DIO subdevices 370*8ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 371*8ffdff6aSGreg Kroah-Hartman * @data: The channel mask and bits to update. 372*8ffdff6aSGreg Kroah-Hartman * 373*8ffdff6aSGreg Kroah-Hartman * Updates @s->state which holds the internal state of the outputs for DIO 374*8ffdff6aSGreg Kroah-Hartman * or DO subdevices (up to 32 channels). @data[0] contains a bit-mask of 375*8ffdff6aSGreg Kroah-Hartman * the channels to be updated. @data[1] contains a bit-mask of those 376*8ffdff6aSGreg Kroah-Hartman * channels to be set to '1'. The caller is responsible for updating the 377*8ffdff6aSGreg Kroah-Hartman * outputs in hardware according to @s->state. As a minimum, the channels 378*8ffdff6aSGreg Kroah-Hartman * in the returned bit-mask need to be updated. 379*8ffdff6aSGreg Kroah-Hartman * 380*8ffdff6aSGreg Kroah-Hartman * Returns @mask with non-existent channels removed. 381*8ffdff6aSGreg Kroah-Hartman */ 382*8ffdff6aSGreg Kroah-Hartman unsigned int comedi_dio_update_state(struct comedi_subdevice *s, 383*8ffdff6aSGreg Kroah-Hartman unsigned int *data) 384*8ffdff6aSGreg Kroah-Hartman { 385*8ffdff6aSGreg Kroah-Hartman unsigned int chanmask = (s->n_chan < 32) ? ((1 << s->n_chan) - 1) 386*8ffdff6aSGreg Kroah-Hartman : 0xffffffff; 387*8ffdff6aSGreg Kroah-Hartman unsigned int mask = data[0] & chanmask; 388*8ffdff6aSGreg Kroah-Hartman unsigned int bits = data[1]; 389*8ffdff6aSGreg Kroah-Hartman 390*8ffdff6aSGreg Kroah-Hartman if (mask) { 391*8ffdff6aSGreg Kroah-Hartman s->state &= ~mask; 392*8ffdff6aSGreg Kroah-Hartman s->state |= (bits & mask); 393*8ffdff6aSGreg Kroah-Hartman } 394*8ffdff6aSGreg Kroah-Hartman 395*8ffdff6aSGreg Kroah-Hartman return mask; 396*8ffdff6aSGreg Kroah-Hartman } 397*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_dio_update_state); 398*8ffdff6aSGreg Kroah-Hartman 399*8ffdff6aSGreg Kroah-Hartman /** 400*8ffdff6aSGreg Kroah-Hartman * comedi_bytes_per_scan_cmd() - Get length of asynchronous command "scan" in 401*8ffdff6aSGreg Kroah-Hartman * bytes 402*8ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 403*8ffdff6aSGreg Kroah-Hartman * @cmd: COMEDI command. 404*8ffdff6aSGreg Kroah-Hartman * 405*8ffdff6aSGreg Kroah-Hartman * Determines the overall scan length according to the subdevice type and the 406*8ffdff6aSGreg Kroah-Hartman * number of channels in the scan for the specified command. 407*8ffdff6aSGreg Kroah-Hartman * 408*8ffdff6aSGreg Kroah-Hartman * For digital input, output or input/output subdevices, samples for 409*8ffdff6aSGreg Kroah-Hartman * multiple channels are assumed to be packed into one or more unsigned 410*8ffdff6aSGreg Kroah-Hartman * short or unsigned int values according to the subdevice's %SDF_LSAMPL 411*8ffdff6aSGreg Kroah-Hartman * flag. For other types of subdevice, samples are assumed to occupy a 412*8ffdff6aSGreg Kroah-Hartman * whole unsigned short or unsigned int according to the %SDF_LSAMPL flag. 413*8ffdff6aSGreg Kroah-Hartman * 414*8ffdff6aSGreg Kroah-Hartman * Returns the overall scan length in bytes. 415*8ffdff6aSGreg Kroah-Hartman */ 416*8ffdff6aSGreg Kroah-Hartman unsigned int comedi_bytes_per_scan_cmd(struct comedi_subdevice *s, 417*8ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd) 418*8ffdff6aSGreg Kroah-Hartman { 419*8ffdff6aSGreg Kroah-Hartman unsigned int num_samples; 420*8ffdff6aSGreg Kroah-Hartman unsigned int bits_per_sample; 421*8ffdff6aSGreg Kroah-Hartman 422*8ffdff6aSGreg Kroah-Hartman switch (s->type) { 423*8ffdff6aSGreg Kroah-Hartman case COMEDI_SUBD_DI: 424*8ffdff6aSGreg Kroah-Hartman case COMEDI_SUBD_DO: 425*8ffdff6aSGreg Kroah-Hartman case COMEDI_SUBD_DIO: 426*8ffdff6aSGreg Kroah-Hartman bits_per_sample = 8 * comedi_bytes_per_sample(s); 427*8ffdff6aSGreg Kroah-Hartman num_samples = DIV_ROUND_UP(cmd->scan_end_arg, bits_per_sample); 428*8ffdff6aSGreg Kroah-Hartman break; 429*8ffdff6aSGreg Kroah-Hartman default: 430*8ffdff6aSGreg Kroah-Hartman num_samples = cmd->scan_end_arg; 431*8ffdff6aSGreg Kroah-Hartman break; 432*8ffdff6aSGreg Kroah-Hartman } 433*8ffdff6aSGreg Kroah-Hartman return comedi_samples_to_bytes(s, num_samples); 434*8ffdff6aSGreg Kroah-Hartman } 435*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_bytes_per_scan_cmd); 436*8ffdff6aSGreg Kroah-Hartman 437*8ffdff6aSGreg Kroah-Hartman /** 438*8ffdff6aSGreg Kroah-Hartman * comedi_bytes_per_scan() - Get length of asynchronous command "scan" in bytes 439*8ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 440*8ffdff6aSGreg Kroah-Hartman * 441*8ffdff6aSGreg Kroah-Hartman * Determines the overall scan length according to the subdevice type and the 442*8ffdff6aSGreg Kroah-Hartman * number of channels in the scan for the current command. 443*8ffdff6aSGreg Kroah-Hartman * 444*8ffdff6aSGreg Kroah-Hartman * For digital input, output or input/output subdevices, samples for 445*8ffdff6aSGreg Kroah-Hartman * multiple channels are assumed to be packed into one or more unsigned 446*8ffdff6aSGreg Kroah-Hartman * short or unsigned int values according to the subdevice's %SDF_LSAMPL 447*8ffdff6aSGreg Kroah-Hartman * flag. For other types of subdevice, samples are assumed to occupy a 448*8ffdff6aSGreg Kroah-Hartman * whole unsigned short or unsigned int according to the %SDF_LSAMPL flag. 449*8ffdff6aSGreg Kroah-Hartman * 450*8ffdff6aSGreg Kroah-Hartman * Returns the overall scan length in bytes. 451*8ffdff6aSGreg Kroah-Hartman */ 452*8ffdff6aSGreg Kroah-Hartman unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s) 453*8ffdff6aSGreg Kroah-Hartman { 454*8ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &s->async->cmd; 455*8ffdff6aSGreg Kroah-Hartman 456*8ffdff6aSGreg Kroah-Hartman return comedi_bytes_per_scan_cmd(s, cmd); 457*8ffdff6aSGreg Kroah-Hartman } 458*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_bytes_per_scan); 459*8ffdff6aSGreg Kroah-Hartman 460*8ffdff6aSGreg Kroah-Hartman static unsigned int __comedi_nscans_left(struct comedi_subdevice *s, 461*8ffdff6aSGreg Kroah-Hartman unsigned int nscans) 462*8ffdff6aSGreg Kroah-Hartman { 463*8ffdff6aSGreg Kroah-Hartman struct comedi_async *async = s->async; 464*8ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &async->cmd; 465*8ffdff6aSGreg Kroah-Hartman 466*8ffdff6aSGreg Kroah-Hartman if (cmd->stop_src == TRIG_COUNT) { 467*8ffdff6aSGreg Kroah-Hartman unsigned int scans_left = 0; 468*8ffdff6aSGreg Kroah-Hartman 469*8ffdff6aSGreg Kroah-Hartman if (async->scans_done < cmd->stop_arg) 470*8ffdff6aSGreg Kroah-Hartman scans_left = cmd->stop_arg - async->scans_done; 471*8ffdff6aSGreg Kroah-Hartman 472*8ffdff6aSGreg Kroah-Hartman if (nscans > scans_left) 473*8ffdff6aSGreg Kroah-Hartman nscans = scans_left; 474*8ffdff6aSGreg Kroah-Hartman } 475*8ffdff6aSGreg Kroah-Hartman return nscans; 476*8ffdff6aSGreg Kroah-Hartman } 477*8ffdff6aSGreg Kroah-Hartman 478*8ffdff6aSGreg Kroah-Hartman /** 479*8ffdff6aSGreg Kroah-Hartman * comedi_nscans_left() - Return the number of scans left in the command 480*8ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 481*8ffdff6aSGreg Kroah-Hartman * @nscans: The expected number of scans or 0 for all available scans. 482*8ffdff6aSGreg Kroah-Hartman * 483*8ffdff6aSGreg Kroah-Hartman * If @nscans is 0, it is set to the number of scans available in the 484*8ffdff6aSGreg Kroah-Hartman * async buffer. 485*8ffdff6aSGreg Kroah-Hartman * 486*8ffdff6aSGreg Kroah-Hartman * If the async command has a stop_src of %TRIG_COUNT, the @nscans will be 487*8ffdff6aSGreg Kroah-Hartman * checked against the number of scans remaining to complete the command. 488*8ffdff6aSGreg Kroah-Hartman * 489*8ffdff6aSGreg Kroah-Hartman * The return value will then be either the expected number of scans or the 490*8ffdff6aSGreg Kroah-Hartman * number of scans remaining to complete the command, whichever is fewer. 491*8ffdff6aSGreg Kroah-Hartman */ 492*8ffdff6aSGreg Kroah-Hartman unsigned int comedi_nscans_left(struct comedi_subdevice *s, 493*8ffdff6aSGreg Kroah-Hartman unsigned int nscans) 494*8ffdff6aSGreg Kroah-Hartman { 495*8ffdff6aSGreg Kroah-Hartman if (nscans == 0) { 496*8ffdff6aSGreg Kroah-Hartman unsigned int nbytes = comedi_buf_read_n_available(s); 497*8ffdff6aSGreg Kroah-Hartman 498*8ffdff6aSGreg Kroah-Hartman nscans = nbytes / comedi_bytes_per_scan(s); 499*8ffdff6aSGreg Kroah-Hartman } 500*8ffdff6aSGreg Kroah-Hartman return __comedi_nscans_left(s, nscans); 501*8ffdff6aSGreg Kroah-Hartman } 502*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_nscans_left); 503*8ffdff6aSGreg Kroah-Hartman 504*8ffdff6aSGreg Kroah-Hartman /** 505*8ffdff6aSGreg Kroah-Hartman * comedi_nsamples_left() - Return the number of samples left in the command 506*8ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 507*8ffdff6aSGreg Kroah-Hartman * @nsamples: The expected number of samples. 508*8ffdff6aSGreg Kroah-Hartman * 509*8ffdff6aSGreg Kroah-Hartman * Returns the number of samples remaining to complete the command, or the 510*8ffdff6aSGreg Kroah-Hartman * specified expected number of samples (@nsamples), whichever is fewer. 511*8ffdff6aSGreg Kroah-Hartman */ 512*8ffdff6aSGreg Kroah-Hartman unsigned int comedi_nsamples_left(struct comedi_subdevice *s, 513*8ffdff6aSGreg Kroah-Hartman unsigned int nsamples) 514*8ffdff6aSGreg Kroah-Hartman { 515*8ffdff6aSGreg Kroah-Hartman struct comedi_async *async = s->async; 516*8ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &async->cmd; 517*8ffdff6aSGreg Kroah-Hartman unsigned long long scans_left; 518*8ffdff6aSGreg Kroah-Hartman unsigned long long samples_left; 519*8ffdff6aSGreg Kroah-Hartman 520*8ffdff6aSGreg Kroah-Hartman if (cmd->stop_src != TRIG_COUNT) 521*8ffdff6aSGreg Kroah-Hartman return nsamples; 522*8ffdff6aSGreg Kroah-Hartman 523*8ffdff6aSGreg Kroah-Hartman scans_left = __comedi_nscans_left(s, cmd->stop_arg); 524*8ffdff6aSGreg Kroah-Hartman if (!scans_left) 525*8ffdff6aSGreg Kroah-Hartman return 0; 526*8ffdff6aSGreg Kroah-Hartman 527*8ffdff6aSGreg Kroah-Hartman samples_left = scans_left * cmd->scan_end_arg - 528*8ffdff6aSGreg Kroah-Hartman comedi_bytes_to_samples(s, async->scan_progress); 529*8ffdff6aSGreg Kroah-Hartman 530*8ffdff6aSGreg Kroah-Hartman if (samples_left < nsamples) 531*8ffdff6aSGreg Kroah-Hartman return samples_left; 532*8ffdff6aSGreg Kroah-Hartman return nsamples; 533*8ffdff6aSGreg Kroah-Hartman } 534*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_nsamples_left); 535*8ffdff6aSGreg Kroah-Hartman 536*8ffdff6aSGreg Kroah-Hartman /** 537*8ffdff6aSGreg Kroah-Hartman * comedi_inc_scan_progress() - Update scan progress in asynchronous command 538*8ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 539*8ffdff6aSGreg Kroah-Hartman * @num_bytes: Amount of data in bytes to increment scan progress. 540*8ffdff6aSGreg Kroah-Hartman * 541*8ffdff6aSGreg Kroah-Hartman * Increments the scan progress by the number of bytes specified by @num_bytes. 542*8ffdff6aSGreg Kroah-Hartman * If the scan progress reaches or exceeds the scan length in bytes, reduce 543*8ffdff6aSGreg Kroah-Hartman * it modulo the scan length in bytes and set the "end of scan" asynchronous 544*8ffdff6aSGreg Kroah-Hartman * event flag (%COMEDI_CB_EOS) to be processed later. 545*8ffdff6aSGreg Kroah-Hartman */ 546*8ffdff6aSGreg Kroah-Hartman void comedi_inc_scan_progress(struct comedi_subdevice *s, 547*8ffdff6aSGreg Kroah-Hartman unsigned int num_bytes) 548*8ffdff6aSGreg Kroah-Hartman { 549*8ffdff6aSGreg Kroah-Hartman struct comedi_async *async = s->async; 550*8ffdff6aSGreg Kroah-Hartman struct comedi_cmd *cmd = &async->cmd; 551*8ffdff6aSGreg Kroah-Hartman unsigned int scan_length = comedi_bytes_per_scan(s); 552*8ffdff6aSGreg Kroah-Hartman 553*8ffdff6aSGreg Kroah-Hartman /* track the 'cur_chan' for non-SDF_PACKED subdevices */ 554*8ffdff6aSGreg Kroah-Hartman if (!(s->subdev_flags & SDF_PACKED)) { 555*8ffdff6aSGreg Kroah-Hartman async->cur_chan += comedi_bytes_to_samples(s, num_bytes); 556*8ffdff6aSGreg Kroah-Hartman async->cur_chan %= cmd->chanlist_len; 557*8ffdff6aSGreg Kroah-Hartman } 558*8ffdff6aSGreg Kroah-Hartman 559*8ffdff6aSGreg Kroah-Hartman async->scan_progress += num_bytes; 560*8ffdff6aSGreg Kroah-Hartman if (async->scan_progress >= scan_length) { 561*8ffdff6aSGreg Kroah-Hartman unsigned int nscans = async->scan_progress / scan_length; 562*8ffdff6aSGreg Kroah-Hartman 563*8ffdff6aSGreg Kroah-Hartman if (async->scans_done < (UINT_MAX - nscans)) 564*8ffdff6aSGreg Kroah-Hartman async->scans_done += nscans; 565*8ffdff6aSGreg Kroah-Hartman else 566*8ffdff6aSGreg Kroah-Hartman async->scans_done = UINT_MAX; 567*8ffdff6aSGreg Kroah-Hartman 568*8ffdff6aSGreg Kroah-Hartman async->scan_progress %= scan_length; 569*8ffdff6aSGreg Kroah-Hartman async->events |= COMEDI_CB_EOS; 570*8ffdff6aSGreg Kroah-Hartman } 571*8ffdff6aSGreg Kroah-Hartman } 572*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_inc_scan_progress); 573*8ffdff6aSGreg Kroah-Hartman 574*8ffdff6aSGreg Kroah-Hartman /** 575*8ffdff6aSGreg Kroah-Hartman * comedi_handle_events() - Handle events and possibly stop acquisition 576*8ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 577*8ffdff6aSGreg Kroah-Hartman * @s: COMEDI subdevice. 578*8ffdff6aSGreg Kroah-Hartman * 579*8ffdff6aSGreg Kroah-Hartman * Handles outstanding asynchronous acquisition event flags associated 580*8ffdff6aSGreg Kroah-Hartman * with the subdevice. Call the subdevice's @s->cancel() handler if the 581*8ffdff6aSGreg Kroah-Hartman * "end of acquisition", "error" or "overflow" event flags are set in order 582*8ffdff6aSGreg Kroah-Hartman * to stop the acquisition at the driver level. 583*8ffdff6aSGreg Kroah-Hartman * 584*8ffdff6aSGreg Kroah-Hartman * Calls comedi_event() to further process the event flags, which may mark 585*8ffdff6aSGreg Kroah-Hartman * the asynchronous command as no longer running, possibly terminated with 586*8ffdff6aSGreg Kroah-Hartman * an error, and may wake up tasks. 587*8ffdff6aSGreg Kroah-Hartman * 588*8ffdff6aSGreg Kroah-Hartman * Return a bit-mask of the handled events. 589*8ffdff6aSGreg Kroah-Hartman */ 590*8ffdff6aSGreg Kroah-Hartman unsigned int comedi_handle_events(struct comedi_device *dev, 591*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s) 592*8ffdff6aSGreg Kroah-Hartman { 593*8ffdff6aSGreg Kroah-Hartman unsigned int events = s->async->events; 594*8ffdff6aSGreg Kroah-Hartman 595*8ffdff6aSGreg Kroah-Hartman if (events == 0) 596*8ffdff6aSGreg Kroah-Hartman return events; 597*8ffdff6aSGreg Kroah-Hartman 598*8ffdff6aSGreg Kroah-Hartman if ((events & COMEDI_CB_CANCEL_MASK) && s->cancel) 599*8ffdff6aSGreg Kroah-Hartman s->cancel(dev, s); 600*8ffdff6aSGreg Kroah-Hartman 601*8ffdff6aSGreg Kroah-Hartman comedi_event(dev, s); 602*8ffdff6aSGreg Kroah-Hartman 603*8ffdff6aSGreg Kroah-Hartman return events; 604*8ffdff6aSGreg Kroah-Hartman } 605*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_handle_events); 606*8ffdff6aSGreg Kroah-Hartman 607*8ffdff6aSGreg Kroah-Hartman static int insn_rw_emulate_bits(struct comedi_device *dev, 608*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s, 609*8ffdff6aSGreg Kroah-Hartman struct comedi_insn *insn, 610*8ffdff6aSGreg Kroah-Hartman unsigned int *data) 611*8ffdff6aSGreg Kroah-Hartman { 612*8ffdff6aSGreg Kroah-Hartman struct comedi_insn _insn; 613*8ffdff6aSGreg Kroah-Hartman unsigned int chan = CR_CHAN(insn->chanspec); 614*8ffdff6aSGreg Kroah-Hartman unsigned int base_chan = (chan < 32) ? 0 : chan; 615*8ffdff6aSGreg Kroah-Hartman unsigned int _data[2]; 616*8ffdff6aSGreg Kroah-Hartman int ret; 617*8ffdff6aSGreg Kroah-Hartman 618*8ffdff6aSGreg Kroah-Hartman memset(_data, 0, sizeof(_data)); 619*8ffdff6aSGreg Kroah-Hartman memset(&_insn, 0, sizeof(_insn)); 620*8ffdff6aSGreg Kroah-Hartman _insn.insn = INSN_BITS; 621*8ffdff6aSGreg Kroah-Hartman _insn.chanspec = base_chan; 622*8ffdff6aSGreg Kroah-Hartman _insn.n = 2; 623*8ffdff6aSGreg Kroah-Hartman _insn.subdev = insn->subdev; 624*8ffdff6aSGreg Kroah-Hartman 625*8ffdff6aSGreg Kroah-Hartman if (insn->insn == INSN_WRITE) { 626*8ffdff6aSGreg Kroah-Hartman if (!(s->subdev_flags & SDF_WRITABLE)) 627*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 628*8ffdff6aSGreg Kroah-Hartman _data[0] = 1 << (chan - base_chan); /* mask */ 629*8ffdff6aSGreg Kroah-Hartman _data[1] = data[0] ? (1 << (chan - base_chan)) : 0; /* bits */ 630*8ffdff6aSGreg Kroah-Hartman } 631*8ffdff6aSGreg Kroah-Hartman 632*8ffdff6aSGreg Kroah-Hartman ret = s->insn_bits(dev, s, &_insn, _data); 633*8ffdff6aSGreg Kroah-Hartman if (ret < 0) 634*8ffdff6aSGreg Kroah-Hartman return ret; 635*8ffdff6aSGreg Kroah-Hartman 636*8ffdff6aSGreg Kroah-Hartman if (insn->insn == INSN_READ) 637*8ffdff6aSGreg Kroah-Hartman data[0] = (_data[1] >> (chan - base_chan)) & 1; 638*8ffdff6aSGreg Kroah-Hartman 639*8ffdff6aSGreg Kroah-Hartman return 1; 640*8ffdff6aSGreg Kroah-Hartman } 641*8ffdff6aSGreg Kroah-Hartman 642*8ffdff6aSGreg Kroah-Hartman static int __comedi_device_postconfig_async(struct comedi_device *dev, 643*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s) 644*8ffdff6aSGreg Kroah-Hartman { 645*8ffdff6aSGreg Kroah-Hartman struct comedi_async *async; 646*8ffdff6aSGreg Kroah-Hartman unsigned int buf_size; 647*8ffdff6aSGreg Kroah-Hartman int ret; 648*8ffdff6aSGreg Kroah-Hartman 649*8ffdff6aSGreg Kroah-Hartman lockdep_assert_held(&dev->mutex); 650*8ffdff6aSGreg Kroah-Hartman if ((s->subdev_flags & (SDF_CMD_READ | SDF_CMD_WRITE)) == 0) { 651*8ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, 652*8ffdff6aSGreg Kroah-Hartman "async subdevices must support SDF_CMD_READ or SDF_CMD_WRITE\n"); 653*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 654*8ffdff6aSGreg Kroah-Hartman } 655*8ffdff6aSGreg Kroah-Hartman if (!s->do_cmdtest) { 656*8ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, 657*8ffdff6aSGreg Kroah-Hartman "async subdevices must have a do_cmdtest() function\n"); 658*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 659*8ffdff6aSGreg Kroah-Hartman } 660*8ffdff6aSGreg Kroah-Hartman if (!s->cancel) 661*8ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, 662*8ffdff6aSGreg Kroah-Hartman "async subdevices should have a cancel() function\n"); 663*8ffdff6aSGreg Kroah-Hartman 664*8ffdff6aSGreg Kroah-Hartman async = kzalloc(sizeof(*async), GFP_KERNEL); 665*8ffdff6aSGreg Kroah-Hartman if (!async) 666*8ffdff6aSGreg Kroah-Hartman return -ENOMEM; 667*8ffdff6aSGreg Kroah-Hartman 668*8ffdff6aSGreg Kroah-Hartman init_waitqueue_head(&async->wait_head); 669*8ffdff6aSGreg Kroah-Hartman s->async = async; 670*8ffdff6aSGreg Kroah-Hartman 671*8ffdff6aSGreg Kroah-Hartman async->max_bufsize = comedi_default_buf_maxsize_kb * 1024; 672*8ffdff6aSGreg Kroah-Hartman buf_size = comedi_default_buf_size_kb * 1024; 673*8ffdff6aSGreg Kroah-Hartman if (buf_size > async->max_bufsize) 674*8ffdff6aSGreg Kroah-Hartman buf_size = async->max_bufsize; 675*8ffdff6aSGreg Kroah-Hartman 676*8ffdff6aSGreg Kroah-Hartman if (comedi_buf_alloc(dev, s, buf_size) < 0) { 677*8ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, "Buffer allocation failed\n"); 678*8ffdff6aSGreg Kroah-Hartman return -ENOMEM; 679*8ffdff6aSGreg Kroah-Hartman } 680*8ffdff6aSGreg Kroah-Hartman if (s->buf_change) { 681*8ffdff6aSGreg Kroah-Hartman ret = s->buf_change(dev, s); 682*8ffdff6aSGreg Kroah-Hartman if (ret < 0) 683*8ffdff6aSGreg Kroah-Hartman return ret; 684*8ffdff6aSGreg Kroah-Hartman } 685*8ffdff6aSGreg Kroah-Hartman 686*8ffdff6aSGreg Kroah-Hartman comedi_alloc_subdevice_minor(s); 687*8ffdff6aSGreg Kroah-Hartman 688*8ffdff6aSGreg Kroah-Hartman return 0; 689*8ffdff6aSGreg Kroah-Hartman } 690*8ffdff6aSGreg Kroah-Hartman 691*8ffdff6aSGreg Kroah-Hartman static int __comedi_device_postconfig(struct comedi_device *dev) 692*8ffdff6aSGreg Kroah-Hartman { 693*8ffdff6aSGreg Kroah-Hartman struct comedi_subdevice *s; 694*8ffdff6aSGreg Kroah-Hartman int ret; 695*8ffdff6aSGreg Kroah-Hartman int i; 696*8ffdff6aSGreg Kroah-Hartman 697*8ffdff6aSGreg Kroah-Hartman lockdep_assert_held(&dev->mutex); 698*8ffdff6aSGreg Kroah-Hartman if (!dev->insn_device_config) 699*8ffdff6aSGreg Kroah-Hartman dev->insn_device_config = insn_device_inval; 700*8ffdff6aSGreg Kroah-Hartman 701*8ffdff6aSGreg Kroah-Hartman if (!dev->get_valid_routes) 702*8ffdff6aSGreg Kroah-Hartman dev->get_valid_routes = get_zero_valid_routes; 703*8ffdff6aSGreg Kroah-Hartman 704*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < dev->n_subdevices; i++) { 705*8ffdff6aSGreg Kroah-Hartman s = &dev->subdevices[i]; 706*8ffdff6aSGreg Kroah-Hartman 707*8ffdff6aSGreg Kroah-Hartman if (s->type == COMEDI_SUBD_UNUSED) 708*8ffdff6aSGreg Kroah-Hartman continue; 709*8ffdff6aSGreg Kroah-Hartman 710*8ffdff6aSGreg Kroah-Hartman if (s->type == COMEDI_SUBD_DO) { 711*8ffdff6aSGreg Kroah-Hartman if (s->n_chan < 32) 712*8ffdff6aSGreg Kroah-Hartman s->io_bits = (1 << s->n_chan) - 1; 713*8ffdff6aSGreg Kroah-Hartman else 714*8ffdff6aSGreg Kroah-Hartman s->io_bits = 0xffffffff; 715*8ffdff6aSGreg Kroah-Hartman } 716*8ffdff6aSGreg Kroah-Hartman 717*8ffdff6aSGreg Kroah-Hartman if (s->len_chanlist == 0) 718*8ffdff6aSGreg Kroah-Hartman s->len_chanlist = 1; 719*8ffdff6aSGreg Kroah-Hartman 720*8ffdff6aSGreg Kroah-Hartman if (s->do_cmd) { 721*8ffdff6aSGreg Kroah-Hartman ret = __comedi_device_postconfig_async(dev, s); 722*8ffdff6aSGreg Kroah-Hartman if (ret) 723*8ffdff6aSGreg Kroah-Hartman return ret; 724*8ffdff6aSGreg Kroah-Hartman } 725*8ffdff6aSGreg Kroah-Hartman 726*8ffdff6aSGreg Kroah-Hartman if (!s->range_table && !s->range_table_list) 727*8ffdff6aSGreg Kroah-Hartman s->range_table = &range_unknown; 728*8ffdff6aSGreg Kroah-Hartman 729*8ffdff6aSGreg Kroah-Hartman if (!s->insn_read && s->insn_bits) 730*8ffdff6aSGreg Kroah-Hartman s->insn_read = insn_rw_emulate_bits; 731*8ffdff6aSGreg Kroah-Hartman if (!s->insn_write && s->insn_bits) 732*8ffdff6aSGreg Kroah-Hartman s->insn_write = insn_rw_emulate_bits; 733*8ffdff6aSGreg Kroah-Hartman 734*8ffdff6aSGreg Kroah-Hartman if (!s->insn_read) 735*8ffdff6aSGreg Kroah-Hartman s->insn_read = insn_inval; 736*8ffdff6aSGreg Kroah-Hartman if (!s->insn_write) 737*8ffdff6aSGreg Kroah-Hartman s->insn_write = insn_inval; 738*8ffdff6aSGreg Kroah-Hartman if (!s->insn_bits) 739*8ffdff6aSGreg Kroah-Hartman s->insn_bits = insn_inval; 740*8ffdff6aSGreg Kroah-Hartman if (!s->insn_config) 741*8ffdff6aSGreg Kroah-Hartman s->insn_config = insn_inval; 742*8ffdff6aSGreg Kroah-Hartman 743*8ffdff6aSGreg Kroah-Hartman if (!s->poll) 744*8ffdff6aSGreg Kroah-Hartman s->poll = poll_invalid; 745*8ffdff6aSGreg Kroah-Hartman } 746*8ffdff6aSGreg Kroah-Hartman 747*8ffdff6aSGreg Kroah-Hartman return 0; 748*8ffdff6aSGreg Kroah-Hartman } 749*8ffdff6aSGreg Kroah-Hartman 750*8ffdff6aSGreg Kroah-Hartman /* do a little post-config cleanup */ 751*8ffdff6aSGreg Kroah-Hartman static int comedi_device_postconfig(struct comedi_device *dev) 752*8ffdff6aSGreg Kroah-Hartman { 753*8ffdff6aSGreg Kroah-Hartman int ret; 754*8ffdff6aSGreg Kroah-Hartman 755*8ffdff6aSGreg Kroah-Hartman lockdep_assert_held(&dev->mutex); 756*8ffdff6aSGreg Kroah-Hartman ret = __comedi_device_postconfig(dev); 757*8ffdff6aSGreg Kroah-Hartman if (ret < 0) 758*8ffdff6aSGreg Kroah-Hartman return ret; 759*8ffdff6aSGreg Kroah-Hartman down_write(&dev->attach_lock); 760*8ffdff6aSGreg Kroah-Hartman dev->attached = true; 761*8ffdff6aSGreg Kroah-Hartman up_write(&dev->attach_lock); 762*8ffdff6aSGreg Kroah-Hartman return 0; 763*8ffdff6aSGreg Kroah-Hartman } 764*8ffdff6aSGreg Kroah-Hartman 765*8ffdff6aSGreg Kroah-Hartman /* 766*8ffdff6aSGreg Kroah-Hartman * Generic recognize function for drivers that register their supported 767*8ffdff6aSGreg Kroah-Hartman * board names. 768*8ffdff6aSGreg Kroah-Hartman * 769*8ffdff6aSGreg Kroah-Hartman * 'driv->board_name' points to a 'const char *' member within the 770*8ffdff6aSGreg Kroah-Hartman * zeroth element of an array of some private board information 771*8ffdff6aSGreg Kroah-Hartman * structure, say 'struct foo_board' containing a member 'const char 772*8ffdff6aSGreg Kroah-Hartman * *board_name' that is initialized to point to a board name string that 773*8ffdff6aSGreg Kroah-Hartman * is one of the candidates matched against this function's 'name' 774*8ffdff6aSGreg Kroah-Hartman * parameter. 775*8ffdff6aSGreg Kroah-Hartman * 776*8ffdff6aSGreg Kroah-Hartman * 'driv->offset' is the size of the private board information 777*8ffdff6aSGreg Kroah-Hartman * structure, say 'sizeof(struct foo_board)', and 'driv->num_names' is 778*8ffdff6aSGreg Kroah-Hartman * the length of the array of private board information structures. 779*8ffdff6aSGreg Kroah-Hartman * 780*8ffdff6aSGreg Kroah-Hartman * If one of the board names in the array of private board information 781*8ffdff6aSGreg Kroah-Hartman * structures matches the name supplied to this function, the function 782*8ffdff6aSGreg Kroah-Hartman * returns a pointer to the pointer to the board name, otherwise it 783*8ffdff6aSGreg Kroah-Hartman * returns NULL. The return value ends up in the 'board_ptr' member of 784*8ffdff6aSGreg Kroah-Hartman * a 'struct comedi_device' that the low-level comedi driver's 785*8ffdff6aSGreg Kroah-Hartman * 'attach()' hook can convert to a point to a particular element of its 786*8ffdff6aSGreg Kroah-Hartman * array of private board information structures by subtracting the 787*8ffdff6aSGreg Kroah-Hartman * offset of the member that points to the board name. (No subtraction 788*8ffdff6aSGreg Kroah-Hartman * is required if the board name pointer is the first member of the 789*8ffdff6aSGreg Kroah-Hartman * private board information structure, which is generally the case.) 790*8ffdff6aSGreg Kroah-Hartman */ 791*8ffdff6aSGreg Kroah-Hartman static void *comedi_recognize(struct comedi_driver *driv, const char *name) 792*8ffdff6aSGreg Kroah-Hartman { 793*8ffdff6aSGreg Kroah-Hartman char **name_ptr = (char **)driv->board_name; 794*8ffdff6aSGreg Kroah-Hartman int i; 795*8ffdff6aSGreg Kroah-Hartman 796*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < driv->num_names; i++) { 797*8ffdff6aSGreg Kroah-Hartman if (strcmp(*name_ptr, name) == 0) 798*8ffdff6aSGreg Kroah-Hartman return name_ptr; 799*8ffdff6aSGreg Kroah-Hartman name_ptr = (void *)name_ptr + driv->offset; 800*8ffdff6aSGreg Kroah-Hartman } 801*8ffdff6aSGreg Kroah-Hartman 802*8ffdff6aSGreg Kroah-Hartman return NULL; 803*8ffdff6aSGreg Kroah-Hartman } 804*8ffdff6aSGreg Kroah-Hartman 805*8ffdff6aSGreg Kroah-Hartman static void comedi_report_boards(struct comedi_driver *driv) 806*8ffdff6aSGreg Kroah-Hartman { 807*8ffdff6aSGreg Kroah-Hartman unsigned int i; 808*8ffdff6aSGreg Kroah-Hartman const char *const *name_ptr; 809*8ffdff6aSGreg Kroah-Hartman 810*8ffdff6aSGreg Kroah-Hartman pr_info("comedi: valid board names for %s driver are:\n", 811*8ffdff6aSGreg Kroah-Hartman driv->driver_name); 812*8ffdff6aSGreg Kroah-Hartman 813*8ffdff6aSGreg Kroah-Hartman name_ptr = driv->board_name; 814*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < driv->num_names; i++) { 815*8ffdff6aSGreg Kroah-Hartman pr_info(" %s\n", *name_ptr); 816*8ffdff6aSGreg Kroah-Hartman name_ptr = (const char **)((char *)name_ptr + driv->offset); 817*8ffdff6aSGreg Kroah-Hartman } 818*8ffdff6aSGreg Kroah-Hartman 819*8ffdff6aSGreg Kroah-Hartman if (driv->num_names == 0) 820*8ffdff6aSGreg Kroah-Hartman pr_info(" %s\n", driv->driver_name); 821*8ffdff6aSGreg Kroah-Hartman } 822*8ffdff6aSGreg Kroah-Hartman 823*8ffdff6aSGreg Kroah-Hartman /** 824*8ffdff6aSGreg Kroah-Hartman * comedi_load_firmware() - Request and load firmware for a device 825*8ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 826*8ffdff6aSGreg Kroah-Hartman * @device: Hardware device. 827*8ffdff6aSGreg Kroah-Hartman * @name: The name of the firmware image. 828*8ffdff6aSGreg Kroah-Hartman * @cb: Callback to the upload the firmware image. 829*8ffdff6aSGreg Kroah-Hartman * @context: Private context from the driver. 830*8ffdff6aSGreg Kroah-Hartman * 831*8ffdff6aSGreg Kroah-Hartman * Sends a firmware request for the hardware device and waits for it. Calls 832*8ffdff6aSGreg Kroah-Hartman * the callback function to upload the firmware to the device, them releases 833*8ffdff6aSGreg Kroah-Hartman * the firmware. 834*8ffdff6aSGreg Kroah-Hartman * 835*8ffdff6aSGreg Kroah-Hartman * Returns 0 on success, -EINVAL if @cb is NULL, or a negative error number 836*8ffdff6aSGreg Kroah-Hartman * from the firmware request or the callback function. 837*8ffdff6aSGreg Kroah-Hartman */ 838*8ffdff6aSGreg Kroah-Hartman int comedi_load_firmware(struct comedi_device *dev, 839*8ffdff6aSGreg Kroah-Hartman struct device *device, 840*8ffdff6aSGreg Kroah-Hartman const char *name, 841*8ffdff6aSGreg Kroah-Hartman int (*cb)(struct comedi_device *dev, 842*8ffdff6aSGreg Kroah-Hartman const u8 *data, size_t size, 843*8ffdff6aSGreg Kroah-Hartman unsigned long context), 844*8ffdff6aSGreg Kroah-Hartman unsigned long context) 845*8ffdff6aSGreg Kroah-Hartman { 846*8ffdff6aSGreg Kroah-Hartman const struct firmware *fw; 847*8ffdff6aSGreg Kroah-Hartman int ret; 848*8ffdff6aSGreg Kroah-Hartman 849*8ffdff6aSGreg Kroah-Hartman if (!cb) 850*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 851*8ffdff6aSGreg Kroah-Hartman 852*8ffdff6aSGreg Kroah-Hartman ret = request_firmware(&fw, name, device); 853*8ffdff6aSGreg Kroah-Hartman if (ret == 0) { 854*8ffdff6aSGreg Kroah-Hartman ret = cb(dev, fw->data, fw->size, context); 855*8ffdff6aSGreg Kroah-Hartman release_firmware(fw); 856*8ffdff6aSGreg Kroah-Hartman } 857*8ffdff6aSGreg Kroah-Hartman 858*8ffdff6aSGreg Kroah-Hartman return ret < 0 ? ret : 0; 859*8ffdff6aSGreg Kroah-Hartman } 860*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_load_firmware); 861*8ffdff6aSGreg Kroah-Hartman 862*8ffdff6aSGreg Kroah-Hartman /** 863*8ffdff6aSGreg Kroah-Hartman * __comedi_request_region() - Request an I/O region for a legacy driver 864*8ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 865*8ffdff6aSGreg Kroah-Hartman * @start: Base address of the I/O region. 866*8ffdff6aSGreg Kroah-Hartman * @len: Length of the I/O region. 867*8ffdff6aSGreg Kroah-Hartman * 868*8ffdff6aSGreg Kroah-Hartman * Requests the specified I/O port region which must start at a non-zero 869*8ffdff6aSGreg Kroah-Hartman * address. 870*8ffdff6aSGreg Kroah-Hartman * 871*8ffdff6aSGreg Kroah-Hartman * Returns 0 on success, -EINVAL if @start is 0, or -EIO if the request 872*8ffdff6aSGreg Kroah-Hartman * fails. 873*8ffdff6aSGreg Kroah-Hartman */ 874*8ffdff6aSGreg Kroah-Hartman int __comedi_request_region(struct comedi_device *dev, 875*8ffdff6aSGreg Kroah-Hartman unsigned long start, unsigned long len) 876*8ffdff6aSGreg Kroah-Hartman { 877*8ffdff6aSGreg Kroah-Hartman if (!start) { 878*8ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, 879*8ffdff6aSGreg Kroah-Hartman "%s: a I/O base address must be specified\n", 880*8ffdff6aSGreg Kroah-Hartman dev->board_name); 881*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 882*8ffdff6aSGreg Kroah-Hartman } 883*8ffdff6aSGreg Kroah-Hartman 884*8ffdff6aSGreg Kroah-Hartman if (!request_region(start, len, dev->board_name)) { 885*8ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, "%s: I/O port conflict (%#lx,%lu)\n", 886*8ffdff6aSGreg Kroah-Hartman dev->board_name, start, len); 887*8ffdff6aSGreg Kroah-Hartman return -EIO; 888*8ffdff6aSGreg Kroah-Hartman } 889*8ffdff6aSGreg Kroah-Hartman 890*8ffdff6aSGreg Kroah-Hartman return 0; 891*8ffdff6aSGreg Kroah-Hartman } 892*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(__comedi_request_region); 893*8ffdff6aSGreg Kroah-Hartman 894*8ffdff6aSGreg Kroah-Hartman /** 895*8ffdff6aSGreg Kroah-Hartman * comedi_request_region() - Request an I/O region for a legacy driver 896*8ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 897*8ffdff6aSGreg Kroah-Hartman * @start: Base address of the I/O region. 898*8ffdff6aSGreg Kroah-Hartman * @len: Length of the I/O region. 899*8ffdff6aSGreg Kroah-Hartman * 900*8ffdff6aSGreg Kroah-Hartman * Requests the specified I/O port region which must start at a non-zero 901*8ffdff6aSGreg Kroah-Hartman * address. 902*8ffdff6aSGreg Kroah-Hartman * 903*8ffdff6aSGreg Kroah-Hartman * On success, @dev->iobase is set to the base address of the region and 904*8ffdff6aSGreg Kroah-Hartman * @dev->iolen is set to its length. 905*8ffdff6aSGreg Kroah-Hartman * 906*8ffdff6aSGreg Kroah-Hartman * Returns 0 on success, -EINVAL if @start is 0, or -EIO if the request 907*8ffdff6aSGreg Kroah-Hartman * fails. 908*8ffdff6aSGreg Kroah-Hartman */ 909*8ffdff6aSGreg Kroah-Hartman int comedi_request_region(struct comedi_device *dev, 910*8ffdff6aSGreg Kroah-Hartman unsigned long start, unsigned long len) 911*8ffdff6aSGreg Kroah-Hartman { 912*8ffdff6aSGreg Kroah-Hartman int ret; 913*8ffdff6aSGreg Kroah-Hartman 914*8ffdff6aSGreg Kroah-Hartman ret = __comedi_request_region(dev, start, len); 915*8ffdff6aSGreg Kroah-Hartman if (ret == 0) { 916*8ffdff6aSGreg Kroah-Hartman dev->iobase = start; 917*8ffdff6aSGreg Kroah-Hartman dev->iolen = len; 918*8ffdff6aSGreg Kroah-Hartman } 919*8ffdff6aSGreg Kroah-Hartman 920*8ffdff6aSGreg Kroah-Hartman return ret; 921*8ffdff6aSGreg Kroah-Hartman } 922*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_request_region); 923*8ffdff6aSGreg Kroah-Hartman 924*8ffdff6aSGreg Kroah-Hartman /** 925*8ffdff6aSGreg Kroah-Hartman * comedi_legacy_detach() - A generic (*detach) function for legacy drivers 926*8ffdff6aSGreg Kroah-Hartman * @dev: COMEDI device. 927*8ffdff6aSGreg Kroah-Hartman * 928*8ffdff6aSGreg Kroah-Hartman * This is a simple, generic 'detach' handler for legacy COMEDI devices that 929*8ffdff6aSGreg Kroah-Hartman * just use a single I/O port region and possibly an IRQ and that don't need 930*8ffdff6aSGreg Kroah-Hartman * any special clean-up for their private device or subdevice storage. It 931*8ffdff6aSGreg Kroah-Hartman * can also be called by a driver-specific 'detach' handler. 932*8ffdff6aSGreg Kroah-Hartman * 933*8ffdff6aSGreg Kroah-Hartman * If @dev->irq is non-zero, the IRQ will be freed. If @dev->iobase and 934*8ffdff6aSGreg Kroah-Hartman * @dev->iolen are both non-zero, the I/O port region will be released. 935*8ffdff6aSGreg Kroah-Hartman */ 936*8ffdff6aSGreg Kroah-Hartman void comedi_legacy_detach(struct comedi_device *dev) 937*8ffdff6aSGreg Kroah-Hartman { 938*8ffdff6aSGreg Kroah-Hartman if (dev->irq) { 939*8ffdff6aSGreg Kroah-Hartman free_irq(dev->irq, dev); 940*8ffdff6aSGreg Kroah-Hartman dev->irq = 0; 941*8ffdff6aSGreg Kroah-Hartman } 942*8ffdff6aSGreg Kroah-Hartman if (dev->iobase && dev->iolen) { 943*8ffdff6aSGreg Kroah-Hartman release_region(dev->iobase, dev->iolen); 944*8ffdff6aSGreg Kroah-Hartman dev->iobase = 0; 945*8ffdff6aSGreg Kroah-Hartman dev->iolen = 0; 946*8ffdff6aSGreg Kroah-Hartman } 947*8ffdff6aSGreg Kroah-Hartman } 948*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_legacy_detach); 949*8ffdff6aSGreg Kroah-Hartman 950*8ffdff6aSGreg Kroah-Hartman int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it) 951*8ffdff6aSGreg Kroah-Hartman { 952*8ffdff6aSGreg Kroah-Hartman struct comedi_driver *driv; 953*8ffdff6aSGreg Kroah-Hartman int ret; 954*8ffdff6aSGreg Kroah-Hartman 955*8ffdff6aSGreg Kroah-Hartman lockdep_assert_held(&dev->mutex); 956*8ffdff6aSGreg Kroah-Hartman if (dev->attached) 957*8ffdff6aSGreg Kroah-Hartman return -EBUSY; 958*8ffdff6aSGreg Kroah-Hartman 959*8ffdff6aSGreg Kroah-Hartman mutex_lock(&comedi_drivers_list_lock); 960*8ffdff6aSGreg Kroah-Hartman for (driv = comedi_drivers; driv; driv = driv->next) { 961*8ffdff6aSGreg Kroah-Hartman if (!try_module_get(driv->module)) 962*8ffdff6aSGreg Kroah-Hartman continue; 963*8ffdff6aSGreg Kroah-Hartman if (driv->num_names) { 964*8ffdff6aSGreg Kroah-Hartman dev->board_ptr = comedi_recognize(driv, it->board_name); 965*8ffdff6aSGreg Kroah-Hartman if (dev->board_ptr) 966*8ffdff6aSGreg Kroah-Hartman break; 967*8ffdff6aSGreg Kroah-Hartman } else if (strcmp(driv->driver_name, it->board_name) == 0) { 968*8ffdff6aSGreg Kroah-Hartman break; 969*8ffdff6aSGreg Kroah-Hartman } 970*8ffdff6aSGreg Kroah-Hartman module_put(driv->module); 971*8ffdff6aSGreg Kroah-Hartman } 972*8ffdff6aSGreg Kroah-Hartman if (!driv) { 973*8ffdff6aSGreg Kroah-Hartman /* recognize has failed if we get here */ 974*8ffdff6aSGreg Kroah-Hartman /* report valid board names before returning error */ 975*8ffdff6aSGreg Kroah-Hartman for (driv = comedi_drivers; driv; driv = driv->next) { 976*8ffdff6aSGreg Kroah-Hartman if (!try_module_get(driv->module)) 977*8ffdff6aSGreg Kroah-Hartman continue; 978*8ffdff6aSGreg Kroah-Hartman comedi_report_boards(driv); 979*8ffdff6aSGreg Kroah-Hartman module_put(driv->module); 980*8ffdff6aSGreg Kroah-Hartman } 981*8ffdff6aSGreg Kroah-Hartman ret = -EIO; 982*8ffdff6aSGreg Kroah-Hartman goto out; 983*8ffdff6aSGreg Kroah-Hartman } 984*8ffdff6aSGreg Kroah-Hartman if (!driv->attach) { 985*8ffdff6aSGreg Kroah-Hartman /* driver does not support manual configuration */ 986*8ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, 987*8ffdff6aSGreg Kroah-Hartman "driver '%s' does not support attach using comedi_config\n", 988*8ffdff6aSGreg Kroah-Hartman driv->driver_name); 989*8ffdff6aSGreg Kroah-Hartman module_put(driv->module); 990*8ffdff6aSGreg Kroah-Hartman ret = -EIO; 991*8ffdff6aSGreg Kroah-Hartman goto out; 992*8ffdff6aSGreg Kroah-Hartman } 993*8ffdff6aSGreg Kroah-Hartman dev->driver = driv; 994*8ffdff6aSGreg Kroah-Hartman dev->board_name = dev->board_ptr ? *(const char **)dev->board_ptr 995*8ffdff6aSGreg Kroah-Hartman : dev->driver->driver_name; 996*8ffdff6aSGreg Kroah-Hartman ret = driv->attach(dev, it); 997*8ffdff6aSGreg Kroah-Hartman if (ret >= 0) 998*8ffdff6aSGreg Kroah-Hartman ret = comedi_device_postconfig(dev); 999*8ffdff6aSGreg Kroah-Hartman if (ret < 0) { 1000*8ffdff6aSGreg Kroah-Hartman comedi_device_detach(dev); 1001*8ffdff6aSGreg Kroah-Hartman module_put(driv->module); 1002*8ffdff6aSGreg Kroah-Hartman } 1003*8ffdff6aSGreg Kroah-Hartman /* On success, the driver module count has been incremented. */ 1004*8ffdff6aSGreg Kroah-Hartman out: 1005*8ffdff6aSGreg Kroah-Hartman mutex_unlock(&comedi_drivers_list_lock); 1006*8ffdff6aSGreg Kroah-Hartman return ret; 1007*8ffdff6aSGreg Kroah-Hartman } 1008*8ffdff6aSGreg Kroah-Hartman 1009*8ffdff6aSGreg Kroah-Hartman /** 1010*8ffdff6aSGreg Kroah-Hartman * comedi_auto_config() - Create a COMEDI device for a hardware device 1011*8ffdff6aSGreg Kroah-Hartman * @hardware_device: Hardware device. 1012*8ffdff6aSGreg Kroah-Hartman * @driver: COMEDI low-level driver for the hardware device. 1013*8ffdff6aSGreg Kroah-Hartman * @context: Driver context for the auto_attach handler. 1014*8ffdff6aSGreg Kroah-Hartman * 1015*8ffdff6aSGreg Kroah-Hartman * Allocates a new COMEDI device for the hardware device and calls the 1016*8ffdff6aSGreg Kroah-Hartman * low-level driver's 'auto_attach' handler to set-up the hardware and 1017*8ffdff6aSGreg Kroah-Hartman * allocate the COMEDI subdevices. Additional "post-configuration" setting 1018*8ffdff6aSGreg Kroah-Hartman * up is performed on successful return from the 'auto_attach' handler. 1019*8ffdff6aSGreg Kroah-Hartman * If the 'auto_attach' handler fails, the low-level driver's 'detach' 1020*8ffdff6aSGreg Kroah-Hartman * handler will be called as part of the clean-up. 1021*8ffdff6aSGreg Kroah-Hartman * 1022*8ffdff6aSGreg Kroah-Hartman * This is usually called from a wrapper function in a bus-specific COMEDI 1023*8ffdff6aSGreg Kroah-Hartman * module, which in turn is usually called from a bus device 'probe' 1024*8ffdff6aSGreg Kroah-Hartman * function in the low-level driver. 1025*8ffdff6aSGreg Kroah-Hartman * 1026*8ffdff6aSGreg Kroah-Hartman * Returns 0 on success, -EINVAL if the parameters are invalid or the 1027*8ffdff6aSGreg Kroah-Hartman * post-configuration determines the driver has set the COMEDI device up 1028*8ffdff6aSGreg Kroah-Hartman * incorrectly, -ENOMEM if failed to allocate memory, -EBUSY if run out of 1029*8ffdff6aSGreg Kroah-Hartman * COMEDI minor device numbers, or some negative error number returned by 1030*8ffdff6aSGreg Kroah-Hartman * the driver's 'auto_attach' handler. 1031*8ffdff6aSGreg Kroah-Hartman */ 1032*8ffdff6aSGreg Kroah-Hartman int comedi_auto_config(struct device *hardware_device, 1033*8ffdff6aSGreg Kroah-Hartman struct comedi_driver *driver, unsigned long context) 1034*8ffdff6aSGreg Kroah-Hartman { 1035*8ffdff6aSGreg Kroah-Hartman struct comedi_device *dev; 1036*8ffdff6aSGreg Kroah-Hartman int ret; 1037*8ffdff6aSGreg Kroah-Hartman 1038*8ffdff6aSGreg Kroah-Hartman if (!hardware_device) { 1039*8ffdff6aSGreg Kroah-Hartman pr_warn("BUG! %s called with NULL hardware_device\n", __func__); 1040*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 1041*8ffdff6aSGreg Kroah-Hartman } 1042*8ffdff6aSGreg Kroah-Hartman if (!driver) { 1043*8ffdff6aSGreg Kroah-Hartman dev_warn(hardware_device, 1044*8ffdff6aSGreg Kroah-Hartman "BUG! %s called with NULL comedi driver\n", __func__); 1045*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 1046*8ffdff6aSGreg Kroah-Hartman } 1047*8ffdff6aSGreg Kroah-Hartman 1048*8ffdff6aSGreg Kroah-Hartman if (!driver->auto_attach) { 1049*8ffdff6aSGreg Kroah-Hartman dev_warn(hardware_device, 1050*8ffdff6aSGreg Kroah-Hartman "BUG! comedi driver '%s' has no auto_attach handler\n", 1051*8ffdff6aSGreg Kroah-Hartman driver->driver_name); 1052*8ffdff6aSGreg Kroah-Hartman return -EINVAL; 1053*8ffdff6aSGreg Kroah-Hartman } 1054*8ffdff6aSGreg Kroah-Hartman 1055*8ffdff6aSGreg Kroah-Hartman dev = comedi_alloc_board_minor(hardware_device); 1056*8ffdff6aSGreg Kroah-Hartman if (IS_ERR(dev)) { 1057*8ffdff6aSGreg Kroah-Hartman dev_warn(hardware_device, 1058*8ffdff6aSGreg Kroah-Hartman "driver '%s' could not create device.\n", 1059*8ffdff6aSGreg Kroah-Hartman driver->driver_name); 1060*8ffdff6aSGreg Kroah-Hartman return PTR_ERR(dev); 1061*8ffdff6aSGreg Kroah-Hartman } 1062*8ffdff6aSGreg Kroah-Hartman /* Note: comedi_alloc_board_minor() locked dev->mutex. */ 1063*8ffdff6aSGreg Kroah-Hartman lockdep_assert_held(&dev->mutex); 1064*8ffdff6aSGreg Kroah-Hartman 1065*8ffdff6aSGreg Kroah-Hartman dev->driver = driver; 1066*8ffdff6aSGreg Kroah-Hartman dev->board_name = dev->driver->driver_name; 1067*8ffdff6aSGreg Kroah-Hartman ret = driver->auto_attach(dev, context); 1068*8ffdff6aSGreg Kroah-Hartman if (ret >= 0) 1069*8ffdff6aSGreg Kroah-Hartman ret = comedi_device_postconfig(dev); 1070*8ffdff6aSGreg Kroah-Hartman 1071*8ffdff6aSGreg Kroah-Hartman if (ret < 0) { 1072*8ffdff6aSGreg Kroah-Hartman dev_warn(hardware_device, 1073*8ffdff6aSGreg Kroah-Hartman "driver '%s' failed to auto-configure device.\n", 1074*8ffdff6aSGreg Kroah-Hartman driver->driver_name); 1075*8ffdff6aSGreg Kroah-Hartman mutex_unlock(&dev->mutex); 1076*8ffdff6aSGreg Kroah-Hartman comedi_release_hardware_device(hardware_device); 1077*8ffdff6aSGreg Kroah-Hartman } else { 1078*8ffdff6aSGreg Kroah-Hartman /* 1079*8ffdff6aSGreg Kroah-Hartman * class_dev should be set properly here 1080*8ffdff6aSGreg Kroah-Hartman * after a successful auto config 1081*8ffdff6aSGreg Kroah-Hartman */ 1082*8ffdff6aSGreg Kroah-Hartman dev_info(dev->class_dev, 1083*8ffdff6aSGreg Kroah-Hartman "driver '%s' has successfully auto-configured '%s'.\n", 1084*8ffdff6aSGreg Kroah-Hartman driver->driver_name, dev->board_name); 1085*8ffdff6aSGreg Kroah-Hartman mutex_unlock(&dev->mutex); 1086*8ffdff6aSGreg Kroah-Hartman } 1087*8ffdff6aSGreg Kroah-Hartman return ret; 1088*8ffdff6aSGreg Kroah-Hartman } 1089*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_auto_config); 1090*8ffdff6aSGreg Kroah-Hartman 1091*8ffdff6aSGreg Kroah-Hartman /** 1092*8ffdff6aSGreg Kroah-Hartman * comedi_auto_unconfig() - Unconfigure auto-allocated COMEDI device 1093*8ffdff6aSGreg Kroah-Hartman * @hardware_device: Hardware device previously passed to 1094*8ffdff6aSGreg Kroah-Hartman * comedi_auto_config(). 1095*8ffdff6aSGreg Kroah-Hartman * 1096*8ffdff6aSGreg Kroah-Hartman * Cleans up and eventually destroys the COMEDI device allocated by 1097*8ffdff6aSGreg Kroah-Hartman * comedi_auto_config() for the same hardware device. As part of this 1098*8ffdff6aSGreg Kroah-Hartman * clean-up, the low-level COMEDI driver's 'detach' handler will be called. 1099*8ffdff6aSGreg Kroah-Hartman * (The COMEDI device itself will persist in an unattached state if it is 1100*8ffdff6aSGreg Kroah-Hartman * still open, until it is released, and any mmapped buffers will persist 1101*8ffdff6aSGreg Kroah-Hartman * until they are munmapped.) 1102*8ffdff6aSGreg Kroah-Hartman * 1103*8ffdff6aSGreg Kroah-Hartman * This is usually called from a wrapper module in a bus-specific COMEDI 1104*8ffdff6aSGreg Kroah-Hartman * module, which in turn is usually set as the bus device 'remove' function 1105*8ffdff6aSGreg Kroah-Hartman * in the low-level COMEDI driver. 1106*8ffdff6aSGreg Kroah-Hartman */ 1107*8ffdff6aSGreg Kroah-Hartman void comedi_auto_unconfig(struct device *hardware_device) 1108*8ffdff6aSGreg Kroah-Hartman { 1109*8ffdff6aSGreg Kroah-Hartman if (!hardware_device) 1110*8ffdff6aSGreg Kroah-Hartman return; 1111*8ffdff6aSGreg Kroah-Hartman comedi_release_hardware_device(hardware_device); 1112*8ffdff6aSGreg Kroah-Hartman } 1113*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_auto_unconfig); 1114*8ffdff6aSGreg Kroah-Hartman 1115*8ffdff6aSGreg Kroah-Hartman /** 1116*8ffdff6aSGreg Kroah-Hartman * comedi_driver_register() - Register a low-level COMEDI driver 1117*8ffdff6aSGreg Kroah-Hartman * @driver: Low-level COMEDI driver. 1118*8ffdff6aSGreg Kroah-Hartman * 1119*8ffdff6aSGreg Kroah-Hartman * The low-level COMEDI driver is added to the list of registered COMEDI 1120*8ffdff6aSGreg Kroah-Hartman * drivers. This is used by the handler for the "/proc/comedi" file and is 1121*8ffdff6aSGreg Kroah-Hartman * also used by the handler for the %COMEDI_DEVCONFIG ioctl to configure 1122*8ffdff6aSGreg Kroah-Hartman * "legacy" COMEDI devices (for those low-level drivers that support it). 1123*8ffdff6aSGreg Kroah-Hartman * 1124*8ffdff6aSGreg Kroah-Hartman * Returns 0. 1125*8ffdff6aSGreg Kroah-Hartman */ 1126*8ffdff6aSGreg Kroah-Hartman int comedi_driver_register(struct comedi_driver *driver) 1127*8ffdff6aSGreg Kroah-Hartman { 1128*8ffdff6aSGreg Kroah-Hartman mutex_lock(&comedi_drivers_list_lock); 1129*8ffdff6aSGreg Kroah-Hartman driver->next = comedi_drivers; 1130*8ffdff6aSGreg Kroah-Hartman comedi_drivers = driver; 1131*8ffdff6aSGreg Kroah-Hartman mutex_unlock(&comedi_drivers_list_lock); 1132*8ffdff6aSGreg Kroah-Hartman 1133*8ffdff6aSGreg Kroah-Hartman return 0; 1134*8ffdff6aSGreg Kroah-Hartman } 1135*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_driver_register); 1136*8ffdff6aSGreg Kroah-Hartman 1137*8ffdff6aSGreg Kroah-Hartman /** 1138*8ffdff6aSGreg Kroah-Hartman * comedi_driver_unregister() - Unregister a low-level COMEDI driver 1139*8ffdff6aSGreg Kroah-Hartman * @driver: Low-level COMEDI driver. 1140*8ffdff6aSGreg Kroah-Hartman * 1141*8ffdff6aSGreg Kroah-Hartman * The low-level COMEDI driver is removed from the list of registered COMEDI 1142*8ffdff6aSGreg Kroah-Hartman * drivers. Detaches any COMEDI devices attached to the driver, which will 1143*8ffdff6aSGreg Kroah-Hartman * result in the low-level driver's 'detach' handler being called for those 1144*8ffdff6aSGreg Kroah-Hartman * devices before this function returns. 1145*8ffdff6aSGreg Kroah-Hartman */ 1146*8ffdff6aSGreg Kroah-Hartman void comedi_driver_unregister(struct comedi_driver *driver) 1147*8ffdff6aSGreg Kroah-Hartman { 1148*8ffdff6aSGreg Kroah-Hartman struct comedi_driver *prev; 1149*8ffdff6aSGreg Kroah-Hartman int i; 1150*8ffdff6aSGreg Kroah-Hartman 1151*8ffdff6aSGreg Kroah-Hartman /* unlink the driver */ 1152*8ffdff6aSGreg Kroah-Hartman mutex_lock(&comedi_drivers_list_lock); 1153*8ffdff6aSGreg Kroah-Hartman if (comedi_drivers == driver) { 1154*8ffdff6aSGreg Kroah-Hartman comedi_drivers = driver->next; 1155*8ffdff6aSGreg Kroah-Hartman } else { 1156*8ffdff6aSGreg Kroah-Hartman for (prev = comedi_drivers; prev->next; prev = prev->next) { 1157*8ffdff6aSGreg Kroah-Hartman if (prev->next == driver) { 1158*8ffdff6aSGreg Kroah-Hartman prev->next = driver->next; 1159*8ffdff6aSGreg Kroah-Hartman break; 1160*8ffdff6aSGreg Kroah-Hartman } 1161*8ffdff6aSGreg Kroah-Hartman } 1162*8ffdff6aSGreg Kroah-Hartman } 1163*8ffdff6aSGreg Kroah-Hartman mutex_unlock(&comedi_drivers_list_lock); 1164*8ffdff6aSGreg Kroah-Hartman 1165*8ffdff6aSGreg Kroah-Hartman /* check for devices using this driver */ 1166*8ffdff6aSGreg Kroah-Hartman for (i = 0; i < COMEDI_NUM_BOARD_MINORS; i++) { 1167*8ffdff6aSGreg Kroah-Hartman struct comedi_device *dev = comedi_dev_get_from_minor(i); 1168*8ffdff6aSGreg Kroah-Hartman 1169*8ffdff6aSGreg Kroah-Hartman if (!dev) 1170*8ffdff6aSGreg Kroah-Hartman continue; 1171*8ffdff6aSGreg Kroah-Hartman 1172*8ffdff6aSGreg Kroah-Hartman mutex_lock(&dev->mutex); 1173*8ffdff6aSGreg Kroah-Hartman if (dev->attached && dev->driver == driver) { 1174*8ffdff6aSGreg Kroah-Hartman if (dev->use_count) 1175*8ffdff6aSGreg Kroah-Hartman dev_warn(dev->class_dev, 1176*8ffdff6aSGreg Kroah-Hartman "BUG! detaching device with use_count=%d\n", 1177*8ffdff6aSGreg Kroah-Hartman dev->use_count); 1178*8ffdff6aSGreg Kroah-Hartman comedi_device_detach(dev); 1179*8ffdff6aSGreg Kroah-Hartman } 1180*8ffdff6aSGreg Kroah-Hartman mutex_unlock(&dev->mutex); 1181*8ffdff6aSGreg Kroah-Hartman comedi_dev_put(dev); 1182*8ffdff6aSGreg Kroah-Hartman } 1183*8ffdff6aSGreg Kroah-Hartman } 1184*8ffdff6aSGreg Kroah-Hartman EXPORT_SYMBOL_GPL(comedi_driver_unregister); 1185