]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/staging/comedi/drivers/comedi_bond.c
Staging: comedi: fix checkpatch.pl issues in comedi_bond.c
[linux-2.6-omap-h63xx.git] / drivers / staging / comedi / drivers / comedi_bond.c
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 /*
25 Driver: comedi_bond
26 Description: A driver to 'bond' (merge) multiple subdevices from multiple
27              devices together as one.
28 Devices:
29 Author: ds
30 Updated: Mon, 10 Oct 00:18:25 -0500
31 Status: works
32
33 This driver allows you to 'bond' (merge) multiple comedi subdevices
34 (coming from possibly difference boards and/or drivers) together.  For
35 example, if you had a board with 2 different DIO subdevices, and
36 another with 1 DIO subdevice, you could 'bond' them with this driver
37 so that they look like one big fat DIO subdevice.  This makes writing
38 applications slightly easier as you don't have to worry about managing
39 different subdevices in the application -- you just worry about
40 indexing one linear array of channel id's.
41
42 Right now only DIO subdevices are supported as that's the personal itch
43 I am scratching with this driver.  If you want to add support for AI and AO
44 subdevs, go right on ahead and do so!
45
46 Commands aren't supported -- although it would be cool if they were.
47
48 Configuration 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
53 /*
54  * The previous block comment is used to automatically generate
55  * documentation in Comedi and Comedilib.  The fields:
56  *
57  * Driver: the name of the driver
58  * Description: a short phrase describing the driver.  Don't list boards.
59  * Devices: a full list of the boards that attempt to be supported by
60  *   the driver.  Format is "(manufacturer) board name [comedi name]",
61  *   where comedi_name is the name that is used to configure the board.
62  *   See the comment near board_name: in the comedi_driver structure
63  *   below.  If (manufacturer) or [comedi name] is missing, the previous
64  *   value is used.
65  * Author: you
66  * Updated: date when the _documentation_ was last updated.  Use 'date -R'
67  *   to get a value for this.
68  * Status: a one-word description of the status.  Valid values are:
69  *   works - driver works correctly on most boards supported, and
70  *     passes comedi_test.
71  *   unknown - unknown.  Usually put there by ds.
72  *   experimental - may not work in any particular release.  Author
73  *     probably wants assistance testing it.
74  *   bitrotten - driver has not been update in a long time, probably
75  *     doesn't work, and probably is missing support for significant
76  *     Comedi interface features.
77  *   untested - author probably wrote it "blind", and is believed to
78  *     work, but no confirmation.
79  *
80  * These headers should be followed by a blank line, and any comments
81  * you wish to say about the driver.  The comment area is the place
82  * to put any known bugs, limitations, unsupported features, supported
83  * command triggers, whether or not commands are supported on particular
84  * subdevices, etc.
85  *
86  * Somewhere in the comment should be information about configuration
87  * options that are used with comedi_config.
88  */
89
90 #include "../comedilib.h"
91 #include "../comedidev.h"
92 #include <linux/string.h>
93
94 /* The maxiumum number of channels per subdevice. */
95 #define MAX_CHANS 256
96
97 #define MODULE_NAME "comedi_bond"
98 #ifdef MODULE_LICENSE
99 MODULE_LICENSE("GPL");
100 #endif
101 #ifndef STR
102 #  define STR1(x) #x
103 #  define STR(x) STR1(x)
104 #endif
105
106 int debug;
107 module_param(debug, int, 0644);
108 MODULE_PARM_DESC(debug, "If true, print extra cryptic debugging output useful"
109                  "only to developers.");
110
111 #define LOG_MSG(x...) printk(KERN_INFO MODULE_NAME": "x)
112 #define DEBUG(x...)                                                     \
113         do {                                                            \
114                 if (debug)                                              \
115                         printk(KERN_DEBUG MODULE_NAME": DEBUG: "x);     \
116         } while (0)
117 #define WARNING(x...)  printk(KERN_WARNING MODULE_NAME ": WARNING: "x)
118 #define ERROR(x...)  printk(KERN_ERR MODULE_NAME ": INTERNAL ERROR: "x)
119 MODULE_AUTHOR("Calin A. Culianu");
120 MODULE_DESCRIPTION(MODULE_NAME "A driver for COMEDI to bond multiple COMEDI "
121                    "devices together as one.  In the words of John Lennon: "
122                    "'And the world will live as one...'");
123
124 /*
125  * Board descriptions for two imaginary boards.  Describing the
126  * boards in this way is optional, and completely driver-dependent.
127  * Some drivers use arrays such as this, other do not.
128  */
129 struct BondingBoard {
130         const char *name;
131 };
132 typedef struct BondingBoard BondingBoard;
133
134 static const BondingBoard bondingBoards[] = {
135         {
136                 .name = MODULE_NAME,
137         },
138 };
139
140 /*
141  * Useful for shorthand access to the particular board structure
142  */
143 #define thisboard ((const BondingBoard *)dev->board_ptr)
144
145 struct BondedDevice {
146         comedi_t *dev;
147         unsigned minor;
148         unsigned subdev;
149         unsigned subdev_type;
150         unsigned nchans;
151         unsigned chanid_offset; /* The offset into our unified linear
152                                    channel-id's of chanid 0 on this
153                                    subdevice. */
154 };
155 typedef struct BondedDevice BondedDevice;
156
157 /* this structure is for data unique to this hardware driver.  If
158    several hardware drivers keep similar information in this structure,
159    feel free to suggest moving the variable to the comedi_device struct.  */
160 struct Private {
161 # define MAX_BOARD_NAME 256
162         char name[MAX_BOARD_NAME];
163         struct BondedDevice **devs;
164         unsigned ndevs;
165         struct BondedDevice *chanIdDevMap[MAX_CHANS];
166         unsigned nchans;
167 };
168 typedef struct Private Private;
169
170 /*
171  * most drivers define the following macro to make it easy to
172  * access the private structure.
173  */
174 #define devpriv ((Private *)dev->private)
175
176 /*
177  * The comedi_driver structure tells the Comedi core module
178  * which functions to call to configure/deconfigure (attach/detach)
179  * the board, and also about the kernel module that contains
180  * the device code.
181  */
182 static int bonding_attach(comedi_device *dev, comedi_devconfig *it);
183 static int bonding_detach(comedi_device *dev);
184 /** Build Private array of all devices.. */
185 static int doDevConfig(comedi_device *dev, comedi_devconfig *it);
186 static void doDevUnconfig(comedi_device *dev);
187 /* Ugly implementation of realloc that always copies memory around -- I'm lazy,
188  * what can I say?  I like to do wasteful memcopies.. :) */
189 static void *Realloc(const void *ptr, size_t len, size_t old_len);
190
191 static comedi_driver driver_bonding = {
192       .driver_name =    MODULE_NAME,
193       .module =         THIS_MODULE,
194       .attach =         bonding_attach,
195       .detach =         bonding_detach,
196         /* It is not necessary to implement the following members if you are
197          * writing a driver for a ISA PnP or PCI card */
198         /* Most drivers will support multiple types of boards by
199          * having an array of board structures.  These were defined
200          * in skel_boards[] above.  Note that the element 'name'
201          * was first in the structure -- Comedi uses this fact to
202          * extract the name of the board without knowing any details
203          * about the structure except for its length.
204          * When a device is attached (by comedi_config), the name
205          * of the device is given to Comedi, and Comedi tries to
206          * match it by going through the list of board names.  If
207          * there is a match, the address of the pointer is put
208          * into dev->board_ptr and driver->attach() is called.
209          *
210          * Note that these are not necessary if you can determine
211          * the type of board in software.  ISA PnP, PCI, and PCMCIA
212          * devices are such boards.
213          */
214       .board_name =     &bondingBoards[0].name,
215       .offset =         sizeof(BondingBoard),
216       .num_names =      sizeof(bondingBoards) / sizeof(BondingBoard),
217 };
218
219 static int bonding_dio_insn_bits(comedi_device *dev, comedi_subdevice *s,
220                                  comedi_insn *insn, lsampl_t *data);
221 static int bonding_dio_insn_config(comedi_device *dev, comedi_subdevice *s,
222                                    comedi_insn *insn, lsampl_t *data);
223
224 /*
225  * Attach is called by the Comedi core to configure the driver
226  * for a particular board.  If you specified a board_name array
227  * in the driver structure, dev->board_ptr contains that
228  * address.
229  */
230 static int bonding_attach(comedi_device *dev, comedi_devconfig *it)
231 {
232         comedi_subdevice *s;
233
234         LOG_MSG("comedi%d\n", dev->minor);
235
236         /*
237          * Allocate the private structure area.  alloc_private() is a
238          * convenient macro defined in comedidev.h.
239          */
240         if (alloc_private(dev, sizeof(Private)) < 0)
241                 return -ENOMEM;
242
243         /*
244          * Setup our bonding from config params.. sets up our Private struct..
245          */
246         if (!doDevConfig(dev, it))
247                 return -EINVAL;
248
249         /*
250          * Initialize dev->board_name.  Note that we can use the "thisboard"
251          * macro now, since we just initialized it in the last line.
252          */
253         dev->board_name = devpriv->name;
254
255         /*
256          * Allocate the subdevice structures.  alloc_subdevice() is a
257          * convenient macro defined in comedidev.h.
258          */
259         if (alloc_subdevices(dev, 1) < 0)
260                 return -ENOMEM;
261
262         s = dev->subdevices + 0;
263         s->type = COMEDI_SUBD_DIO;
264         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
265         s->n_chan = devpriv->nchans;
266         s->maxdata = 1;
267         s->range_table = &range_digital;
268         s->insn_bits = bonding_dio_insn_bits;
269         s->insn_config = bonding_dio_insn_config;
270
271         LOG_MSG("attached with %u DIO channels coming from %u different "
272                 "subdevices all bonded together.  "
273                 "John Lennon would be proud!\n",
274                 devpriv->nchans, devpriv->ndevs);
275
276         return 1;
277 }
278
279 /*
280  * _detach is called to deconfigure a device.  It should deallocate
281  * resources.
282  * This function is also called when _attach() fails, so it should be
283  * careful not to release resources that were not necessarily
284  * allocated by _attach().  dev->private and dev->subdevices are
285  * deallocated automatically by the core.
286  */
287 static int bonding_detach(comedi_device *dev)
288 {
289         LOG_MSG("comedi%d: remove\n", dev->minor);
290         doDevUnconfig(dev);
291         return 0;
292 }
293
294 /* DIO devices are slightly special.  Although it is possible to
295  * implement the insn_read/insn_write interface, it is much more
296  * useful to applications if you implement the insn_bits interface.
297  * This allows packed reading/writing of the DIO channels.  The
298  * comedi core can convert between insn_bits and insn_read/write */
299 static int bonding_dio_insn_bits(comedi_device *dev, comedi_subdevice *s,
300                                  comedi_insn *insn, lsampl_t *data)
301 {
302 #define LSAMPL_BITS (sizeof(lsampl_t)*8)
303         unsigned nchans = LSAMPL_BITS, num_done = 0, i;
304         if (insn->n != 2)
305                 return -EINVAL;
306
307         if (devpriv->nchans < nchans)
308                 nchans = devpriv->nchans;
309
310         /* The insn data is a mask in data[0] and the new data
311          * in data[1], each channel cooresponding to a bit. */
312         for (i = 0; num_done < nchans && i < devpriv->ndevs; ++i) {
313                 BondedDevice *bdev = devpriv->devs[i];
314                 /* Grab the channel mask and data of only the bits corresponding
315                    to this subdevice.. need to shift them to zero position of
316                    course. */
317                 /* Bits corresponding to this subdev. */
318                 lsampl_t subdevMask = ((1 << bdev->nchans) - 1);
319                 lsampl_t writeMask, dataBits;
320
321                 /* Argh, we have >= LSAMPL_BITS chans.. take all bits */
322                 if (bdev->nchans >= LSAMPL_BITS)
323                         subdevMask = (lsampl_t) (-1);
324
325                 writeMask = (data[0] >> num_done) & subdevMask;
326                 dataBits = (data[1] >> num_done) & subdevMask;
327
328                 /* Read/Write the new digital lines */
329                 if (comedi_dio_bitfield(bdev->dev, bdev->subdev, writeMask,
330                                 &dataBits) != 2)
331                         return -EINVAL;
332
333                 /* Make room for the new bits in data[1], the return value */
334                 data[1] &= ~(subdevMask << num_done);
335                 /* Put the bits in the return value */
336                 data[1] |= (dataBits & subdevMask) << num_done;
337                 /* Save the new bits to the saved state.. */
338                 s->state = data[1];
339
340                 num_done += bdev->nchans;
341         }
342
343         return insn->n;
344 }
345
346 static int bonding_dio_insn_config(comedi_device *dev, comedi_subdevice *s,
347                                    comedi_insn *insn, lsampl_t *data)
348 {
349         int chan = CR_CHAN(insn->chanspec), ret, io_bits = s->io_bits;
350         unsigned int io;
351         BondedDevice *bdev;
352
353         if (chan < 0 || chan >= devpriv->nchans)
354                 return -EINVAL;
355         bdev = devpriv->chanIdDevMap[chan];
356
357         /* The input or output configuration of each digital line is
358          * configured by a special insn_config instruction.  chanspec
359          * contains the channel to be changed, and data[0] contains the
360          * value COMEDI_INPUT or COMEDI_OUTPUT. */
361         switch (data[0]) {
362         case INSN_CONFIG_DIO_OUTPUT:
363                 io = COMEDI_OUTPUT;     /* is this really necessary? */
364                 io_bits |= 1 << chan;
365                 break;
366         case INSN_CONFIG_DIO_INPUT:
367                 io = COMEDI_INPUT;      /* is this really necessary? */
368                 io_bits &= ~(1 << chan);
369                 break;
370         case INSN_CONFIG_DIO_QUERY:
371                 data[1] =
372                         (io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
373                 return insn->n;
374                 break;
375         default:
376                 return -EINVAL;
377                 break;
378         }
379         /* 'real' channel id for this subdev.. */
380         chan -= bdev->chanid_offset;
381         ret = comedi_dio_config(bdev->dev, bdev->subdev, chan, io);
382         if (ret != 1)
383                 return -EINVAL;
384         /* Finally, save the new io_bits values since we didn't get
385            an error above. */
386         s->io_bits = io_bits;
387         return insn->n;
388 }
389
390 static void *Realloc(const void *oldmem, size_t newlen, size_t oldlen)
391 {
392         void *newmem = kmalloc(newlen, GFP_KERNEL);
393
394         if (newmem && oldmem)
395                 memcpy(newmem, oldmem, min(oldlen, newlen));
396         kfree(oldmem);
397         return newmem;
398 }
399
400 static int doDevConfig(comedi_device *dev, comedi_devconfig *it)
401 {
402         int i;
403         comedi_t *devs_opened[COMEDI_NUM_BOARD_MINORS];
404
405         memset(devs_opened, 0, sizeof(devs_opened));
406         devpriv->name[0] = 0;;
407         /* Loop through all comedi devices specified on the command-line,
408            building our device list */
409         for (i = 0; i < COMEDI_NDEVCONFOPTS && (!i || it->options[i]); ++i) {
410                 char file[] = "/dev/comediXXXXXX";
411                 int minor = it->options[i];
412                 comedi_t *d;
413                 int sdev = -1, nchans, tmp;
414                 BondedDevice *bdev = 0;
415
416                 if (minor < 0 || minor > COMEDI_NUM_BOARD_MINORS) {
417                         ERROR("Minor %d is invalid!\n", minor);
418                         return 0;
419                 }
420                 if (minor == dev->minor) {
421                         ERROR("Cannot bond this driver to itself!\n");
422                         return 0;
423                 }
424                 if (devs_opened[minor]) {
425                         ERROR("Minor %d specified more than once!\n", minor);
426                         return 0;
427                 }
428
429                 snprintf(file, sizeof(file), "/dev/comedi%u", minor);
430                 file[sizeof(file) - 1] = 0;
431
432                 d = devs_opened[minor] = comedi_open(file);
433
434                 if (!d) {
435                         ERROR("Minor %u could not be opened\n", minor);
436                         return 0;
437                 }
438
439                 /* Do DIO, as that's all we support now.. */
440                 while ((sdev = comedi_find_subdevice_by_type(d, COMEDI_SUBD_DIO,
441                                         sdev + 1)) > -1) {
442                         nchans = comedi_get_n_channels(d, sdev);
443                         if (nchans <= 0) {
444                                 ERROR("comedi_get_n_channels() returned %d "
445                                       "on minor %u subdev %d!\n",
446                                       nchans, minor, sdev);
447                                 return 0;
448                         }
449                         bdev = kmalloc(sizeof(*bdev), GFP_KERNEL);
450                         if (!bdev) {
451                                 ERROR("Out of memory.\n");
452                                 return 0;
453                         }
454                         bdev->dev = d;
455                         bdev->minor = minor;
456                         bdev->subdev = sdev;
457                         bdev->subdev_type = COMEDI_SUBD_DIO;
458                         bdev->nchans = nchans;
459                         bdev->chanid_offset = devpriv->nchans;
460
461                         /* map channel id's to BondedDevice * pointer.. */
462                         while (nchans--)
463                                 devpriv->chanIdDevMap[devpriv->nchans++] = bdev;
464
465                         /* Now put bdev pointer at end of devpriv->devs array
466                          * list.. */
467
468                         /* ergh.. ugly.. we need to realloc :(  */
469                         tmp = devpriv->ndevs * sizeof(bdev);
470                         devpriv->devs =
471                                 Realloc(devpriv->devs,
472                                 ++devpriv->ndevs * sizeof(bdev), tmp);
473                         if (!devpriv->devs) {
474                                 ERROR("Could not allocate memory. "
475                                       "Out of memory?");
476                                 return 0;
477                         }
478
479                         devpriv->devs[devpriv->ndevs - 1] = bdev;
480                         {
481         /** Append dev:subdev to devpriv->name */
482                                 char buf[20];
483                                 int left =
484                                         MAX_BOARD_NAME - strlen(devpriv->name) -
485                                         1;
486                                 snprintf(buf, sizeof(buf), "%d:%d ", dev->minor,
487                                         bdev->subdev);
488                                 buf[sizeof(buf) - 1] = 0;
489                                 strncat(devpriv->name, buf, left);
490                         }
491
492                 }
493         }
494
495         if (!devpriv->nchans) {
496                 ERROR("No channels found!\n");
497                 return 0;
498         }
499
500         return 1;
501 }
502
503 static void doDevUnconfig(comedi_device *dev)
504 {
505         unsigned long devs_closed = 0;
506
507         if (devpriv) {
508                 while (devpriv->ndevs-- && devpriv->devs) {
509                         BondedDevice *bdev = devpriv->devs[devpriv->ndevs];
510                         if (!bdev)
511                                 continue;
512                         if (!(devs_closed & (0x1 << bdev->minor))) {
513                                 comedi_close(bdev->dev);
514                                 devs_closed |= (0x1 << bdev->minor);
515                         }
516                         kfree(bdev);
517                 }
518                 kfree(devpriv->devs);
519                 devpriv->devs = 0;
520                 kfree(devpriv);
521                 dev->private = 0;
522         }
523 }
524
525 int __init init(void)
526 {
527         return comedi_driver_register(&driver_bonding);
528 }
529
530 void __exit cleanup(void)
531 {
532         comedi_driver_unregister(&driver_bonding);
533 }
534
535 module_init(init);
536 module_exit(cleanup);