diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/comedi_bond.c')
-rw-r--r-- | drivers/staging/comedi/drivers/comedi_bond.c | 347 |
1 files changed, 0 insertions, 347 deletions
diff --git a/drivers/staging/comedi/drivers/comedi_bond.c b/drivers/staging/comedi/drivers/comedi_bond.c deleted file mode 100644 index 4392b5927a99..000000000000 --- a/drivers/staging/comedi/drivers/comedi_bond.c +++ /dev/null @@ -1,347 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * comedi_bond.c - * A Comedi driver to 'bond' or merge multiple drivers and devices as one. - * - * COMEDI - Linux Control and Measurement Device Interface - * Copyright (C) 2000 David A. Schleef <ds@schleef.org> - * Copyright (C) 2005 Calin A. Culianu <calin@ajvar.org> - */ - -/* - * Driver: comedi_bond - * Description: A driver to 'bond' (merge) multiple subdevices from multiple - * devices together as one. - * Devices: - * Author: ds - * Updated: Mon, 10 Oct 00:18:25 -0500 - * Status: works - * - * This driver allows you to 'bond' (merge) multiple comedi subdevices - * (coming from possibly difference boards and/or drivers) together. For - * example, if you had a board with 2 different DIO subdevices, and - * another with 1 DIO subdevice, you could 'bond' them with this driver - * so that they look like one big fat DIO subdevice. This makes writing - * applications slightly easier as you don't have to worry about managing - * different subdevices in the application -- you just worry about - * indexing one linear array of channel id's. - * - * Right now only DIO subdevices are supported as that's the personal itch - * I am scratching with this driver. If you want to add support for AI and AO - * subdevs, go right on ahead and do so! - * - * Commands aren't supported -- although it would be cool if they were. - * - * Configuration Options: - * List of comedi-minors to bond. All subdevices of the same type - * within each minor will be concatenated together in the order given here. - */ - -#include <linux/module.h> -#include <linux/string.h> -#include <linux/slab.h> -#include "../comedi.h" -#include "../comedilib.h" -#include "../comedidev.h" - -struct bonded_device { - struct comedi_device *dev; - unsigned int minor; - unsigned int subdev; - unsigned int nchans; -}; - -struct comedi_bond_private { - char name[256]; - struct bonded_device **devs; - unsigned int ndevs; - unsigned int nchans; -}; - -static int bonding_dio_insn_bits(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) -{ - struct comedi_bond_private *devpriv = dev->private; - unsigned int n_left, n_done, base_chan; - unsigned int write_mask, data_bits; - struct bonded_device **devs; - - write_mask = data[0]; - data_bits = data[1]; - base_chan = CR_CHAN(insn->chanspec); - /* do a maximum of 32 channels, starting from base_chan. */ - n_left = devpriv->nchans - base_chan; - if (n_left > 32) - n_left = 32; - - n_done = 0; - devs = devpriv->devs; - do { - struct bonded_device *bdev = *devs++; - - if (base_chan < bdev->nchans) { - /* base channel falls within bonded device */ - unsigned int b_chans, b_mask, b_write_mask, b_data_bits; - int ret; - - /* - * Get num channels to do for bonded device and set - * up mask and data bits for bonded device. - */ - b_chans = bdev->nchans - base_chan; - if (b_chans > n_left) - b_chans = n_left; - b_mask = (b_chans < 32) ? ((1 << b_chans) - 1) - : 0xffffffff; - b_write_mask = (write_mask >> n_done) & b_mask; - b_data_bits = (data_bits >> n_done) & b_mask; - /* Read/Write the new digital lines. */ - ret = comedi_dio_bitfield2(bdev->dev, bdev->subdev, - b_write_mask, &b_data_bits, - base_chan); - if (ret < 0) - return ret; - /* Place read bits into data[1]. */ - data[1] &= ~(b_mask << n_done); - data[1] |= (b_data_bits & b_mask) << n_done; - /* - * Set up for following bonded device (if still have - * channels to read/write). - */ - base_chan = 0; - n_done += b_chans; - n_left -= b_chans; - } else { - /* Skip bonded devices before base channel. */ - base_chan -= bdev->nchans; - } - } while (n_left); - - return insn->n; -} - -static int bonding_dio_insn_config(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) -{ - struct comedi_bond_private *devpriv = dev->private; - unsigned int chan = CR_CHAN(insn->chanspec); - int ret; - struct bonded_device *bdev; - struct bonded_device **devs; - - /* - * Locate bonded subdevice and adjust channel. - */ - devs = devpriv->devs; - for (bdev = *devs++; chan >= bdev->nchans; bdev = *devs++) - chan -= bdev->nchans; - - /* - * The input or output configuration of each digital line is - * configured by a special insn_config instruction. chanspec - * contains the channel to be changed, and data[0] contains the - * configuration instruction INSN_CONFIG_DIO_OUTPUT, - * INSN_CONFIG_DIO_INPUT or INSN_CONFIG_DIO_QUERY. - * - * Note that INSN_CONFIG_DIO_OUTPUT == COMEDI_OUTPUT, - * and INSN_CONFIG_DIO_INPUT == COMEDI_INPUT. This is deliberate ;) - */ - switch (data[0]) { - case INSN_CONFIG_DIO_OUTPUT: - case INSN_CONFIG_DIO_INPUT: - ret = comedi_dio_config(bdev->dev, bdev->subdev, chan, data[0]); - break; - case INSN_CONFIG_DIO_QUERY: - ret = comedi_dio_get_config(bdev->dev, bdev->subdev, chan, - &data[1]); - break; - default: - ret = -EINVAL; - break; - } - if (ret >= 0) - ret = insn->n; - return ret; -} - -static int do_dev_config(struct comedi_device *dev, struct comedi_devconfig *it) -{ - struct comedi_bond_private *devpriv = dev->private; - DECLARE_BITMAP(devs_opened, COMEDI_NUM_BOARD_MINORS); - int i; - - memset(&devs_opened, 0, sizeof(devs_opened)); - devpriv->name[0] = 0; - /* - * Loop through all comedi devices specified on the command-line, - * building our device list. - */ - for (i = 0; i < COMEDI_NDEVCONFOPTS && (!i || it->options[i]); ++i) { - char file[sizeof("/dev/comediXXXXXX")]; - int minor = it->options[i]; - struct comedi_device *d; - int sdev = -1, nchans; - struct bonded_device *bdev; - struct bonded_device **devs; - - if (minor < 0 || minor >= COMEDI_NUM_BOARD_MINORS) { - dev_err(dev->class_dev, - "Minor %d is invalid!\n", minor); - return -EINVAL; - } - if (minor == dev->minor) { - dev_err(dev->class_dev, - "Cannot bond this driver to itself!\n"); - return -EINVAL; - } - if (test_and_set_bit(minor, devs_opened)) { - dev_err(dev->class_dev, - "Minor %d specified more than once!\n", minor); - return -EINVAL; - } - - snprintf(file, sizeof(file), "/dev/comedi%d", minor); - file[sizeof(file) - 1] = 0; - - d = comedi_open(file); - - if (!d) { - dev_err(dev->class_dev, - "Minor %u could not be opened\n", minor); - return -ENODEV; - } - - /* Do DIO, as that's all we support now.. */ - while ((sdev = comedi_find_subdevice_by_type(d, COMEDI_SUBD_DIO, - sdev + 1)) > -1) { - nchans = comedi_get_n_channels(d, sdev); - if (nchans <= 0) { - dev_err(dev->class_dev, - "comedi_get_n_channels() returned %d on minor %u subdev %d!\n", - nchans, minor, sdev); - return -EINVAL; - } - bdev = kmalloc(sizeof(*bdev), GFP_KERNEL); - if (!bdev) - return -ENOMEM; - - bdev->dev = d; - bdev->minor = minor; - bdev->subdev = sdev; - bdev->nchans = nchans; - devpriv->nchans += nchans; - - /* - * Now put bdev pointer at end of devpriv->devs array - * list.. - */ - - /* ergh.. ugly.. we need to realloc :( */ - devs = krealloc(devpriv->devs, - (devpriv->ndevs + 1) * sizeof(*devs), - GFP_KERNEL); - if (!devs) { - dev_err(dev->class_dev, - "Could not allocate memory. Out of memory?\n"); - kfree(bdev); - return -ENOMEM; - } - devpriv->devs = devs; - devpriv->devs[devpriv->ndevs++] = bdev; - { - /* Append dev:subdev to devpriv->name */ - char buf[20]; - - snprintf(buf, sizeof(buf), "%u:%u ", - bdev->minor, bdev->subdev); - strlcat(devpriv->name, buf, - sizeof(devpriv->name)); - } - } - } - - if (!devpriv->nchans) { - dev_err(dev->class_dev, "No channels found!\n"); - return -EINVAL; - } - - return 0; -} - -static int bonding_attach(struct comedi_device *dev, - struct comedi_devconfig *it) -{ - struct comedi_bond_private *devpriv; - struct comedi_subdevice *s; - int ret; - - devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); - if (!devpriv) - return -ENOMEM; - - /* - * Setup our bonding from config params.. sets up our private struct.. - */ - ret = do_dev_config(dev, it); - if (ret) - return ret; - - dev->board_name = devpriv->name; - - ret = comedi_alloc_subdevices(dev, 1); - if (ret) - return ret; - - s = &dev->subdevices[0]; - s->type = COMEDI_SUBD_DIO; - s->subdev_flags = SDF_READABLE | SDF_WRITABLE; - s->n_chan = devpriv->nchans; - s->maxdata = 1; - s->range_table = &range_digital; - s->insn_bits = bonding_dio_insn_bits; - s->insn_config = bonding_dio_insn_config; - - dev_info(dev->class_dev, - "%s: %s attached, %u channels from %u devices\n", - dev->driver->driver_name, dev->board_name, - devpriv->nchans, devpriv->ndevs); - - return 0; -} - -static void bonding_detach(struct comedi_device *dev) -{ - struct comedi_bond_private *devpriv = dev->private; - - if (devpriv && devpriv->devs) { - DECLARE_BITMAP(devs_closed, COMEDI_NUM_BOARD_MINORS); - - memset(&devs_closed, 0, sizeof(devs_closed)); - while (devpriv->ndevs--) { - struct bonded_device *bdev; - - bdev = devpriv->devs[devpriv->ndevs]; - if (!bdev) - continue; - if (!test_and_set_bit(bdev->minor, devs_closed)) - comedi_close(bdev->dev); - kfree(bdev); - } - kfree(devpriv->devs); - devpriv->devs = NULL; - } -} - -static struct comedi_driver bonding_driver = { - .driver_name = "comedi_bond", - .module = THIS_MODULE, - .attach = bonding_attach, - .detach = bonding_detach, -}; -module_comedi_driver(bonding_driver); - -MODULE_AUTHOR("Calin A. Culianu"); -MODULE_DESCRIPTION("comedi_bond: A driver for COMEDI to bond multiple COMEDI devices together as one."); -MODULE_LICENSE("GPL"); |