staging: comedi: remove FSF address from boilerplate text
[deliverable/linux.git] / drivers / staging / comedi / drivers / comedi_bond.c
CommitLineData
3cf840f6
DS
1/*
2 comedi/drivers/comedi_bond.c
3 A Comedi driver to 'bond' or merge multiple drivers and devices as one.
4
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
7 Copyright (C) 2005 Calin A. Culianu <calin@ajvar.org>
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
3cf840f6
DS
18*/
19/*
20Driver: comedi_bond
e7f2aa34
GKH
21Description: A driver to 'bond' (merge) multiple subdevices from multiple
22 devices together as one.
3cf840f6
DS
23Devices:
24Author: ds
25Updated: Mon, 10 Oct 00:18:25 -0500
26Status: works
27
28This driver allows you to 'bond' (merge) multiple comedi subdevices
29(coming from possibly difference boards and/or drivers) together. For
30example, if you had a board with 2 different DIO subdevices, and
31another with 1 DIO subdevice, you could 'bond' them with this driver
32so that they look like one big fat DIO subdevice. This makes writing
33applications slightly easier as you don't have to worry about managing
34different subdevices in the application -- you just worry about
35indexing one linear array of channel id's.
36
37Right now only DIO subdevices are supported as that's the personal itch
38I am scratching with this driver. If you want to add support for AI and AO
39subdevs, go right on ahead and do so!
40
41Commands aren't supported -- although it would be cool if they were.
42
43Configuration Options:
44 List of comedi-minors to bond. All subdevices of the same type
45 within each minor will be concatenated together in the order given here.
46*/
47
3b6b25b5
GKH
48#include <linux/string.h>
49#include <linux/slab.h>
e2a0eab0 50#include "../comedi.h"
3cf840f6
DS
51#include "../comedilib.h"
52#include "../comedidev.h"
3cf840f6
DS
53
54/* The maxiumum number of channels per subdevice. */
55#define MAX_CHANS 256
56
3cf840f6 57struct BondedDevice {
472dfe77 58 struct comedi_device *dev;
3cf840f6
DS
59 unsigned minor;
60 unsigned subdev;
61 unsigned subdev_type;
62 unsigned nchans;
e7f2aa34
GKH
63 unsigned chanid_offset; /* The offset into our unified linear
64 channel-id's of chanid 0 on this
65 subdevice. */
3cf840f6 66};
3cf840f6
DS
67
68/* this structure is for data unique to this hardware driver. If
69 several hardware drivers keep similar information in this structure,
71b5f4f1 70 feel free to suggest moving the variable to the struct comedi_device struct. */
a62a1764 71struct comedi_bond_private {
3cf840f6
DS
72# define MAX_BOARD_NAME 256
73 char name[MAX_BOARD_NAME];
74 struct BondedDevice **devs;
75 unsigned ndevs;
76 struct BondedDevice *chanIdDevMap[MAX_CHANS];
77 unsigned nchans;
78};
3cf840f6 79
3cf840f6
DS
80/* DIO devices are slightly special. Although it is possible to
81 * implement the insn_read/insn_write interface, it is much more
82 * useful to applications if you implement the insn_bits interface.
83 * This allows packed reading/writing of the DIO channels. The
84 * comedi core can convert between insn_bits and insn_read/write */
0a85b6f0
MT
85static int bonding_dio_insn_bits(struct comedi_device *dev,
86 struct comedi_subdevice *s,
90035c08 87 struct comedi_insn *insn, unsigned int *data)
3cf840f6 88{
a62a1764 89 struct comedi_bond_private *devpriv = dev->private;
790c5541 90#define LSAMPL_BITS (sizeof(unsigned int)*8)
3cf840f6 91 unsigned nchans = LSAMPL_BITS, num_done = 0, i;
3cf840f6
DS
92
93 if (devpriv->nchans < nchans)
94 nchans = devpriv->nchans;
95
96 /* The insn data is a mask in data[0] and the new data
97 * in data[1], each channel cooresponding to a bit. */
98 for (i = 0; num_done < nchans && i < devpriv->ndevs; ++i) {
ff534766 99 struct BondedDevice *bdev = devpriv->devs[i];
3cf840f6
DS
100 /* Grab the channel mask and data of only the bits corresponding
101 to this subdevice.. need to shift them to zero position of
102 course. */
e7f2aa34 103 /* Bits corresponding to this subdev. */
790c5541
BP
104 unsigned int subdevMask = ((1 << bdev->nchans) - 1);
105 unsigned int writeMask, dataBits;
3cf840f6
DS
106
107 /* Argh, we have >= LSAMPL_BITS chans.. take all bits */
108 if (bdev->nchans >= LSAMPL_BITS)
0a85b6f0 109 subdevMask = (unsigned int)(-1);
3cf840f6
DS
110
111 writeMask = (data[0] >> num_done) & subdevMask;
112 dataBits = (data[1] >> num_done) & subdevMask;
113
114 /* Read/Write the new digital lines */
115 if (comedi_dio_bitfield(bdev->dev, bdev->subdev, writeMask,
0a85b6f0 116 &dataBits) != 2)
3cf840f6
DS
117 return -EINVAL;
118
119 /* Make room for the new bits in data[1], the return value */
120 data[1] &= ~(subdevMask << num_done);
121 /* Put the bits in the return value */
122 data[1] |= (dataBits & subdevMask) << num_done;
123 /* Save the new bits to the saved state.. */
124 s->state = data[1];
125
126 num_done += bdev->nchans;
127 }
128
129 return insn->n;
130}
131
0a85b6f0
MT
132static int bonding_dio_insn_config(struct comedi_device *dev,
133 struct comedi_subdevice *s,
90035c08 134 struct comedi_insn *insn, unsigned int *data)
3cf840f6 135{
a62a1764 136 struct comedi_bond_private *devpriv = dev->private;
3cf840f6
DS
137 int chan = CR_CHAN(insn->chanspec), ret, io_bits = s->io_bits;
138 unsigned int io;
ff534766 139 struct BondedDevice *bdev;
3cf840f6
DS
140
141 if (chan < 0 || chan >= devpriv->nchans)
142 return -EINVAL;
143 bdev = devpriv->chanIdDevMap[chan];
144
145 /* The input or output configuration of each digital line is
146 * configured by a special insn_config instruction. chanspec
147 * contains the channel to be changed, and data[0] contains the
148 * value COMEDI_INPUT or COMEDI_OUTPUT. */
149 switch (data[0]) {
150 case INSN_CONFIG_DIO_OUTPUT:
151 io = COMEDI_OUTPUT; /* is this really necessary? */
152 io_bits |= 1 << chan;
153 break;
154 case INSN_CONFIG_DIO_INPUT:
155 io = COMEDI_INPUT; /* is this really necessary? */
156 io_bits &= ~(1 << chan);
157 break;
158 case INSN_CONFIG_DIO_QUERY:
159 data[1] =
0a85b6f0 160 (io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
3cf840f6
DS
161 return insn->n;
162 break;
163 default:
164 return -EINVAL;
165 break;
166 }
e7f2aa34
GKH
167 /* 'real' channel id for this subdev.. */
168 chan -= bdev->chanid_offset;
3cf840f6
DS
169 ret = comedi_dio_config(bdev->dev, bdev->subdev, chan, io);
170 if (ret != 1)
171 return -EINVAL;
172 /* Finally, save the new io_bits values since we didn't get
173 an error above. */
174 s->io_bits = io_bits;
175 return insn->n;
176}
177
178static void *Realloc(const void *oldmem, size_t newlen, size_t oldlen)
179{
3cf840f6 180 void *newmem = kmalloc(newlen, GFP_KERNEL);
e7f2aa34 181
3cf840f6 182 if (newmem && oldmem)
e7f2aa34
GKH
183 memcpy(newmem, oldmem, min(oldlen, newlen));
184 kfree(oldmem);
3cf840f6
DS
185 return newmem;
186}
187
0707bb04 188static int doDevConfig(struct comedi_device *dev, struct comedi_devconfig *it)
3cf840f6 189{
a62a1764 190 struct comedi_bond_private *devpriv = dev->private;
3cf840f6 191 int i;
472dfe77 192 struct comedi_device *devs_opened[COMEDI_NUM_BOARD_MINORS];
3cf840f6
DS
193
194 memset(devs_opened, 0, sizeof(devs_opened));
859171ca 195 devpriv->name[0] = 0;
3cf840f6
DS
196 /* Loop through all comedi devices specified on the command-line,
197 building our device list */
198 for (i = 0; i < COMEDI_NDEVCONFOPTS && (!i || it->options[i]); ++i) {
199 char file[] = "/dev/comediXXXXXX";
200 int minor = it->options[i];
472dfe77 201 struct comedi_device *d;
3cf840f6 202 int sdev = -1, nchans, tmp;
246c5418 203 struct BondedDevice *bdev = NULL;
3cf840f6 204
5d3aed74 205 if (minor < 0 || minor >= COMEDI_NUM_BOARD_MINORS) {
fdb2a66f
HS
206 dev_err(dev->class_dev,
207 "Minor %d is invalid!\n", minor);
3cf840f6
DS
208 return 0;
209 }
210 if (minor == dev->minor) {
fdb2a66f
HS
211 dev_err(dev->class_dev,
212 "Cannot bond this driver to itself!\n");
3cf840f6
DS
213 return 0;
214 }
215 if (devs_opened[minor]) {
fdb2a66f
HS
216 dev_err(dev->class_dev,
217 "Minor %d specified more than once!\n", minor);
3cf840f6
DS
218 return 0;
219 }
220
221 snprintf(file, sizeof(file), "/dev/comedi%u", minor);
222 file[sizeof(file) - 1] = 0;
223
224 d = devs_opened[minor] = comedi_open(file);
225
226 if (!d) {
fdb2a66f
HS
227 dev_err(dev->class_dev,
228 "Minor %u could not be opened\n", minor);
3cf840f6
DS
229 return 0;
230 }
231
232 /* Do DIO, as that's all we support now.. */
233 while ((sdev = comedi_find_subdevice_by_type(d, COMEDI_SUBD_DIO,
0a85b6f0 234 sdev + 1)) > -1) {
e7f2aa34
GKH
235 nchans = comedi_get_n_channels(d, sdev);
236 if (nchans <= 0) {
fdb2a66f
HS
237 dev_err(dev->class_dev,
238 "comedi_get_n_channels() returned %d on minor %u subdev %d!\n",
239 nchans, minor, sdev);
3cf840f6
DS
240 return 0;
241 }
242 bdev = kmalloc(sizeof(*bdev), GFP_KERNEL);
78110bb8 243 if (!bdev)
3cf840f6 244 return 0;
78110bb8 245
3cf840f6
DS
246 bdev->dev = d;
247 bdev->minor = minor;
248 bdev->subdev = sdev;
249 bdev->subdev_type = COMEDI_SUBD_DIO;
250 bdev->nchans = nchans;
251 bdev->chanid_offset = devpriv->nchans;
252
253 /* map channel id's to BondedDevice * pointer.. */
254 while (nchans--)
255 devpriv->chanIdDevMap[devpriv->nchans++] = bdev;
256
e7f2aa34
GKH
257 /* Now put bdev pointer at end of devpriv->devs array
258 * list.. */
3cf840f6
DS
259
260 /* ergh.. ugly.. we need to realloc :( */
261 tmp = devpriv->ndevs * sizeof(bdev);
262 devpriv->devs =
0a85b6f0
MT
263 Realloc(devpriv->devs,
264 ++devpriv->ndevs * sizeof(bdev), tmp);
3cf840f6 265 if (!devpriv->devs) {
fdb2a66f
HS
266 dev_err(dev->class_dev,
267 "Could not allocate memory. Out of memory?\n");
3cf840f6
DS
268 return 0;
269 }
270
271 devpriv->devs[devpriv->ndevs - 1] = bdev;
272 {
273 /** Append dev:subdev to devpriv->name */
274 char buf[20];
275 int left =
0a85b6f0 276 MAX_BOARD_NAME - strlen(devpriv->name) - 1;
3cf840f6 277 snprintf(buf, sizeof(buf), "%d:%d ", dev->minor,
0a85b6f0 278 bdev->subdev);
3cf840f6
DS
279 buf[sizeof(buf) - 1] = 0;
280 strncat(devpriv->name, buf, left);
281 }
282
283 }
284 }
285
286 if (!devpriv->nchans) {
fdb2a66f 287 dev_err(dev->class_dev, "No channels found!\n");
3cf840f6
DS
288 return 0;
289 }
290
291 return 1;
292}
293
ecdc3e0d
HS
294static int bonding_attach(struct comedi_device *dev,
295 struct comedi_devconfig *it)
3cf840f6 296{
a62a1764 297 struct comedi_bond_private *devpriv;
ecdc3e0d 298 struct comedi_subdevice *s;
8b6c5694 299 int ret;
ecdc3e0d 300
c34fa261
HS
301 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
302 if (!devpriv)
303 return -ENOMEM;
304 dev->private = devpriv;
ecdc3e0d
HS
305
306 /*
a62a1764 307 * Setup our bonding from config params.. sets up our private struct..
ecdc3e0d
HS
308 */
309 if (!doDevConfig(dev, it))
310 return -EINVAL;
311
ecdc3e0d
HS
312 dev->board_name = devpriv->name;
313
8b6c5694
HS
314 ret = comedi_alloc_subdevices(dev, 1);
315 if (ret)
316 return ret;
ecdc3e0d 317
379585ab 318 s = &dev->subdevices[0];
ecdc3e0d
HS
319 s->type = COMEDI_SUBD_DIO;
320 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
321 s->n_chan = devpriv->nchans;
322 s->maxdata = 1;
323 s->range_table = &range_digital;
324 s->insn_bits = bonding_dio_insn_bits;
325 s->insn_config = bonding_dio_insn_config;
326
fdb2a66f
HS
327 dev_info(dev->class_dev,
328 "%s: %s attached, %u channels from %u devices\n",
329 dev->driver->driver_name, dev->board_name,
ecdc3e0d
HS
330 devpriv->nchans, devpriv->ndevs);
331
332 return 1;
3cf840f6
DS
333}
334
484ecc95 335static void bonding_detach(struct comedi_device *dev)
3cf840f6 336{
a62a1764 337 struct comedi_bond_private *devpriv = dev->private;
484ecc95
HS
338 unsigned long devs_closed = 0;
339
340 if (devpriv) {
341 while (devpriv->ndevs-- && devpriv->devs) {
342 struct BondedDevice *bdev;
343
344 bdev = devpriv->devs[devpriv->ndevs];
345 if (!bdev)
346 continue;
347 if (!(devs_closed & (0x1 << bdev->minor))) {
348 comedi_close(bdev->dev);
349 devs_closed |= (0x1 << bdev->minor);
350 }
351 kfree(bdev);
352 }
353 kfree(devpriv->devs);
354 devpriv->devs = NULL;
355 kfree(devpriv);
356 dev->private = NULL;
357 }
3cf840f6
DS
358}
359
ecdc3e0d
HS
360static struct comedi_driver bonding_driver = {
361 .driver_name = "comedi_bond",
362 .module = THIS_MODULE,
363 .attach = bonding_attach,
364 .detach = bonding_detach,
ecdc3e0d
HS
365};
366module_comedi_driver(bonding_driver);
367
368MODULE_AUTHOR("Calin A. Culianu");
fa38126b 369MODULE_DESCRIPTION("comedi_bond: A driver for COMEDI to bond multiple COMEDI "
ecdc3e0d
HS
370 "devices together as one. In the words of John Lennon: "
371 "'And the world will live as one...'");
372MODULE_LICENSE("GPL");
This page took 0.872632 seconds and 5 git commands to generate.