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