]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/comedi/drivers/skel.c
staging: comedi: skel: replace pr_...() with dev_...()
[karo-tx-linux.git] / drivers / staging / comedi / drivers / skel.c
1 /*
2     comedi/drivers/skel.c
3     Skeleton code for a Comedi driver
4
5     COMEDI - Linux Control and Measurement Device Interface
6     Copyright (C) 2000 David A. Schleef <ds@schleef.org>
7
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17
18     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 */
23 /*
24 Driver: skel
25 Description: Skeleton driver, an example for driver writers
26 Devices:
27 Author: ds
28 Updated: Mon, 18 Mar 2002 15:34:01 -0800
29 Status: works
30
31 This driver is a documented example on how Comedi drivers are
32 written.
33
34 Configuration Options:
35   none
36 */
37
38 /*
39  * The previous block comment is used to automatically generate
40  * documentation in Comedi and Comedilib.  The fields:
41  *
42  *  Driver: the name of the driver
43  *  Description: a short phrase describing the driver.  Don't list boards.
44  *  Devices: a full list of the boards that attempt to be supported by
45  *    the driver.  Format is "(manufacturer) board name [comedi name]",
46  *    where comedi_name is the name that is used to configure the board.
47  *    See the comment near board_name: in the struct comedi_driver structure
48  *    below.  If (manufacturer) or [comedi name] is missing, the previous
49  *    value is used.
50  *  Author: you
51  *  Updated: date when the _documentation_ was last updated.  Use 'date -R'
52  *    to get a value for this.
53  *  Status: a one-word description of the status.  Valid values are:
54  *    works - driver works correctly on most boards supported, and
55  *      passes comedi_test.
56  *    unknown - unknown.  Usually put there by ds.
57  *    experimental - may not work in any particular release.  Author
58  *      probably wants assistance testing it.
59  *    bitrotten - driver has not been update in a long time, probably
60  *      doesn't work, and probably is missing support for significant
61  *      Comedi interface features.
62  *    untested - author probably wrote it "blind", and is believed to
63  *      work, but no confirmation.
64  *
65  * These headers should be followed by a blank line, and any comments
66  * you wish to say about the driver.  The comment area is the place
67  * to put any known bugs, limitations, unsupported features, supported
68  * command triggers, whether or not commands are supported on particular
69  * subdevices, etc.
70  *
71  * Somewhere in the comment should be information about configuration
72  * options that are used with comedi_config.
73  */
74
75 #include "../comedidev.h"
76
77 #include <linux/pci.h>          /* for PCI devices */
78
79 #include "comedi_fc.h"
80
81 /* Imaginary registers for the imaginary board */
82
83 #define SKEL_SIZE 0
84
85 #define SKEL_START_AI_CONV      0
86 #define SKEL_AI_READ            0
87
88 /*
89  * Board descriptions for two imaginary boards.  Describing the
90  * boards in this way is optional, and completely driver-dependent.
91  * Some drivers use arrays such as this, other do not.
92  */
93 struct skel_board {
94         const char *name;
95         int ai_chans;
96         int ai_bits;
97         int have_dio;
98 };
99
100 static const struct skel_board skel_boards[] = {
101         {
102          .name = "skel-100",
103          .ai_chans = 16,
104          .ai_bits = 12,
105          .have_dio = 1,
106          },
107         {
108          .name = "skel-200",
109          .ai_chans = 8,
110          .ai_bits = 16,
111          .have_dio = 0,
112          },
113 };
114
115 /* This is used by modprobe to translate PCI IDs to drivers.  Should
116  * only be used for PCI and ISA-PnP devices */
117 /* Please add your PCI vendor ID to comedidev.h, and it will be forwarded
118  * upstream. */
119 #define PCI_VENDOR_ID_SKEL 0xdafe
120 static DEFINE_PCI_DEVICE_TABLE(skel_pci_table) = {
121         { PCI_DEVICE(PCI_VENDOR_ID_SKEL, 0x0100) },
122         { PCI_DEVICE(PCI_VENDOR_ID_SKEL, 0x0200) },
123         { 0 }
124 };
125
126 MODULE_DEVICE_TABLE(pci, skel_pci_table);
127
128 /* this structure is for data unique to this hardware driver.  If
129    several hardware drivers keep similar information in this structure,
130    feel free to suggest moving the variable to the struct comedi_device struct.
131  */
132 struct skel_private {
133
134         int data;
135
136         /* would be useful for a PCI device */
137         struct pci_dev *pci_dev;
138
139         /* Used for AO readback */
140         unsigned int ao_readback[2];
141 };
142
143 /*
144  * The struct comedi_driver structure tells the Comedi core module
145  * which functions to call to configure/deconfigure (attach/detach)
146  * the board, and also about the kernel module that contains
147  * the device code.
148  */
149 static int skel_attach(struct comedi_device *dev, struct comedi_devconfig *it);
150 static void skel_detach(struct comedi_device *dev);
151 static struct comedi_driver driver_skel = {
152         .driver_name = "dummy",
153         .module = THIS_MODULE,
154         .attach = skel_attach,
155         .detach = skel_detach,
156 /* It is not necessary to implement the following members if you are
157  * writing a driver for a ISA PnP or PCI card */
158         /* Most drivers will support multiple types of boards by
159          * having an array of board structures.  These were defined
160          * in skel_boards[] above.  Note that the element 'name'
161          * was first in the structure -- Comedi uses this fact to
162          * extract the name of the board without knowing any details
163          * about the structure except for its length.
164          * When a device is attached (by comedi_config), the name
165          * of the device is given to Comedi, and Comedi tries to
166          * match it by going through the list of board names.  If
167          * there is a match, the address of the pointer is put
168          * into dev->board_ptr and driver->attach() is called.
169          *
170          * Note that these are not necessary if you can determine
171          * the type of board in software.  ISA PnP, PCI, and PCMCIA
172          * devices are such boards.
173          */
174         .board_name = &skel_boards[0].name,
175         .offset = sizeof(struct skel_board),
176         .num_names = ARRAY_SIZE(skel_boards),
177 };
178
179 static int skel_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
180                          struct comedi_insn *insn, unsigned int *data);
181 static int skel_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
182                          struct comedi_insn *insn, unsigned int *data);
183 static int skel_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
184                          struct comedi_insn *insn, unsigned int *data);
185 static int skel_dio_insn_bits(struct comedi_device *dev,
186                               struct comedi_subdevice *s,
187                               struct comedi_insn *insn, unsigned int *data);
188 static int skel_dio_insn_config(struct comedi_device *dev,
189                                 struct comedi_subdevice *s,
190                                 struct comedi_insn *insn, unsigned int *data);
191 static int skel_ai_cmdtest(struct comedi_device *dev,
192                            struct comedi_subdevice *s, struct comedi_cmd *cmd);
193 static int skel_ns_to_timer(unsigned int *ns, int round);
194
195 /*
196  * Attach is called by the Comedi core to configure the driver
197  * for a particular board.  If you specified a board_name array
198  * in the driver structure, dev->board_ptr contains that
199  * address.
200  */
201 static int skel_attach(struct comedi_device *dev, struct comedi_devconfig *it)
202 {
203         const struct skel_board *thisboard;
204         struct skel_private *devpriv;
205         struct comedi_subdevice *s;
206         int ret;
207
208 /*
209  * If you can probe the device to determine what device in a series
210  * it is, this is the place to do it.  Otherwise, dev->board_ptr
211  * should already be initialized.
212  */
213         /* dev->board_ptr = skel_probe(dev, it); */
214
215         thisboard = comedi_board(dev);
216 /*
217  * Initialize dev->board_name.
218  */
219         dev->board_name = thisboard->name;
220
221         /* Allocate the private data */
222         devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
223         if (!devpriv)
224                 return -ENOMEM;
225         dev->private = devpriv;
226
227         ret = comedi_alloc_subdevices(dev, 3);
228         if (ret)
229                 return ret;
230
231         s = &dev->subdevices[0];
232         /* dev->read_subdev=s; */
233         /* analog input subdevice */
234         s->type = COMEDI_SUBD_AI;
235         /* we support single-ended (ground) and differential */
236         s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
237         s->n_chan = thisboard->ai_chans;
238         s->maxdata = (1 << thisboard->ai_bits) - 1;
239         s->range_table = &range_bipolar10;
240         s->len_chanlist = 16;   /* This is the maximum chanlist length that
241                                    the board can handle */
242         s->insn_read = skel_ai_rinsn;
243 /*
244 *       s->subdev_flags |= SDF_CMD_READ;
245 *       s->do_cmd = skel_ai_cmd;
246 */
247         s->do_cmdtest = skel_ai_cmdtest;
248
249         s = &dev->subdevices[1];
250         /* analog output subdevice */
251         s->type = COMEDI_SUBD_AO;
252         s->subdev_flags = SDF_WRITABLE;
253         s->n_chan = 1;
254         s->maxdata = 0xffff;
255         s->range_table = &range_bipolar5;
256         s->insn_write = skel_ao_winsn;
257         s->insn_read = skel_ao_rinsn;
258
259         s = &dev->subdevices[2];
260         /* digital i/o subdevice */
261         if (thisboard->have_dio) {
262                 s->type = COMEDI_SUBD_DIO;
263                 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
264                 s->n_chan = 16;
265                 s->maxdata = 1;
266                 s->range_table = &range_digital;
267                 s->insn_bits = skel_dio_insn_bits;
268                 s->insn_config = skel_dio_insn_config;
269         } else {
270                 s->type = COMEDI_SUBD_UNUSED;
271         }
272
273         dev_info(dev->class_dev, "skel: attached\n");
274
275         return 0;
276 }
277
278 /*
279  * _detach is called to deconfigure a device.  It should deallocate
280  * resources.
281  * This function is also called when _attach() fails, so it should be
282  * careful not to release resources that were not necessarily
283  * allocated by _attach().  dev->private and dev->subdevices are
284  * deallocated automatically by the core.
285  */
286 static void skel_detach(struct comedi_device *dev)
287 {
288 }
289
290 /*
291  * "instructions" read/write data in "one-shot" or "software-triggered"
292  * mode.
293  */
294 static int skel_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
295                          struct comedi_insn *insn, unsigned int *data)
296 {
297         const struct skel_board *thisboard = comedi_board(dev);
298         int n, i;
299         unsigned int d;
300         unsigned int status;
301
302         /* a typical programming sequence */
303
304         /* write channel to multiplexer */
305         /* outw(chan,dev->iobase + SKEL_MUX); */
306
307         /* don't wait for mux to settle */
308
309         /* convert n samples */
310         for (n = 0; n < insn->n; n++) {
311                 /* trigger conversion */
312                 /* outw(0,dev->iobase + SKEL_CONVERT); */
313
314 #define TIMEOUT 100
315                 /* wait for conversion to end */
316                 for (i = 0; i < TIMEOUT; i++) {
317                         status = 1;
318                         /* status = inb(dev->iobase + SKEL_STATUS); */
319                         if (status)
320                                 break;
321                 }
322                 if (i == TIMEOUT) {
323                         dev_warn(dev->class_dev, "ai timeout\n");
324                         return -ETIMEDOUT;
325                 }
326
327                 /* read data */
328                 /* d = inw(dev->iobase + SKEL_AI_DATA); */
329                 d = 0;
330
331                 /* mangle the data as necessary */
332                 d ^= 1 << (thisboard->ai_bits - 1);
333
334                 data[n] = d;
335         }
336
337         /* return the number of samples read/written */
338         return n;
339 }
340
341 /*
342  * cmdtest tests a particular command to see if it is valid.
343  * Using the cmdtest ioctl, a user can create a valid cmd
344  * and then have it executes by the cmd ioctl.
345  *
346  * cmdtest returns 1,2,3,4 or 0, depending on which tests
347  * the command passes.
348  */
349 static int skel_ai_cmdtest(struct comedi_device *dev,
350                            struct comedi_subdevice *s,
351                            struct comedi_cmd *cmd)
352 {
353         int err = 0;
354         int tmp;
355
356         /* Step 1 : check if triggers are trivially valid */
357
358         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
359         err |= cfc_check_trigger_src(&cmd->scan_begin_src,
360                                         TRIG_TIMER | TRIG_EXT);
361         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
362         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
363         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
364
365         if (err)
366                 return 1;
367
368         /* Step 2a : make sure trigger sources are unique */
369
370         err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
371         err |= cfc_check_trigger_is_unique(cmd->convert_src);
372         err |= cfc_check_trigger_is_unique(cmd->stop_src);
373
374         /* Step 2b : and mutually compatible */
375
376         if (err)
377                 return 2;
378
379         /* step 3: make sure arguments are trivially compatible */
380
381         if (cmd->start_arg != 0) {
382                 cmd->start_arg = 0;
383                 err++;
384         }
385 #define MAX_SPEED       10000   /* in nanoseconds */
386 #define MIN_SPEED       1000000000      /* in nanoseconds */
387
388         if (cmd->scan_begin_src == TRIG_TIMER) {
389                 if (cmd->scan_begin_arg < MAX_SPEED) {
390                         cmd->scan_begin_arg = MAX_SPEED;
391                         err++;
392                 }
393                 if (cmd->scan_begin_arg > MIN_SPEED) {
394                         cmd->scan_begin_arg = MIN_SPEED;
395                         err++;
396                 }
397         } else {
398                 /* external trigger */
399                 /* should be level/edge, hi/lo specification here */
400                 /* should specify multiple external triggers */
401                 if (cmd->scan_begin_arg > 9) {
402                         cmd->scan_begin_arg = 9;
403                         err++;
404                 }
405         }
406         if (cmd->convert_src == TRIG_TIMER) {
407                 if (cmd->convert_arg < MAX_SPEED) {
408                         cmd->convert_arg = MAX_SPEED;
409                         err++;
410                 }
411                 if (cmd->convert_arg > MIN_SPEED) {
412                         cmd->convert_arg = MIN_SPEED;
413                         err++;
414                 }
415         } else {
416                 /* external trigger */
417                 /* see above */
418                 if (cmd->convert_arg > 9) {
419                         cmd->convert_arg = 9;
420                         err++;
421                 }
422         }
423
424         if (cmd->scan_end_arg != cmd->chanlist_len) {
425                 cmd->scan_end_arg = cmd->chanlist_len;
426                 err++;
427         }
428         if (cmd->stop_src == TRIG_COUNT) {
429                 if (cmd->stop_arg > 0x00ffffff) {
430                         cmd->stop_arg = 0x00ffffff;
431                         err++;
432                 }
433         } else {
434                 /* TRIG_NONE */
435                 if (cmd->stop_arg != 0) {
436                         cmd->stop_arg = 0;
437                         err++;
438                 }
439         }
440
441         if (err)
442                 return 3;
443
444         /* step 4: fix up any arguments */
445
446         if (cmd->scan_begin_src == TRIG_TIMER) {
447                 tmp = cmd->scan_begin_arg;
448                 skel_ns_to_timer(&cmd->scan_begin_arg,
449                                  cmd->flags & TRIG_ROUND_MASK);
450                 if (tmp != cmd->scan_begin_arg)
451                         err++;
452         }
453         if (cmd->convert_src == TRIG_TIMER) {
454                 tmp = cmd->convert_arg;
455                 skel_ns_to_timer(&cmd->convert_arg,
456                                  cmd->flags & TRIG_ROUND_MASK);
457                 if (tmp != cmd->convert_arg)
458                         err++;
459                 if (cmd->scan_begin_src == TRIG_TIMER &&
460                     cmd->scan_begin_arg <
461                     cmd->convert_arg * cmd->scan_end_arg) {
462                         cmd->scan_begin_arg =
463                             cmd->convert_arg * cmd->scan_end_arg;
464                         err++;
465                 }
466         }
467
468         if (err)
469                 return 4;
470
471         return 0;
472 }
473
474 /* This function doesn't require a particular form, this is just
475  * what happens to be used in some of the drivers.  It should
476  * convert ns nanoseconds to a counter value suitable for programming
477  * the device.  Also, it should adjust ns so that it cooresponds to
478  * the actual time that the device will use. */
479 static int skel_ns_to_timer(unsigned int *ns, int round)
480 {
481         /* trivial timer */
482         /* if your timing is done through two cascaded timers, the
483          * i8253_cascade_ns_to_timer() function in 8253.h can be
484          * very helpful.  There are also i8254_load() and i8254_mm_load()
485          * which can be used to load values into the ubiquitous 8254 counters
486          */
487
488         return *ns;
489 }
490
491 static int skel_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
492                          struct comedi_insn *insn, unsigned int *data)
493 {
494         struct skel_private *devpriv = dev->private;
495         int i;
496         int chan = CR_CHAN(insn->chanspec);
497
498         /* Writing a list of values to an AO channel is probably not
499          * very useful, but that's how the interface is defined. */
500         for (i = 0; i < insn->n; i++) {
501                 /* a typical programming sequence */
502                 /* outw(data[i],dev->iobase + SKEL_DA0 + chan); */
503                 devpriv->ao_readback[chan] = data[i];
504         }
505
506         /* return the number of samples read/written */
507         return i;
508 }
509
510 /* AO subdevices should have a read insn as well as a write insn.
511  * Usually this means copying a value stored in devpriv. */
512 static int skel_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
513                          struct comedi_insn *insn, unsigned int *data)
514 {
515         struct skel_private *devpriv = dev->private;
516         int i;
517         int chan = CR_CHAN(insn->chanspec);
518
519         for (i = 0; i < insn->n; i++)
520                 data[i] = devpriv->ao_readback[chan];
521
522         return i;
523 }
524
525 /* DIO devices are slightly special.  Although it is possible to
526  * implement the insn_read/insn_write interface, it is much more
527  * useful to applications if you implement the insn_bits interface.
528  * This allows packed reading/writing of the DIO channels.  The
529  * comedi core can convert between insn_bits and insn_read/write */
530 static int skel_dio_insn_bits(struct comedi_device *dev,
531                               struct comedi_subdevice *s,
532                               struct comedi_insn *insn, unsigned int *data)
533 {
534         /* The insn data is a mask in data[0] and the new data
535          * in data[1], each channel cooresponding to a bit. */
536         if (data[0]) {
537                 s->state &= ~data[0];
538                 s->state |= data[0] & data[1];
539                 /* Write out the new digital output lines */
540                 /* outw(s->state,dev->iobase + SKEL_DIO); */
541         }
542
543         /* on return, data[1] contains the value of the digital
544          * input and output lines. */
545         /* data[1]=inw(dev->iobase + SKEL_DIO); */
546         /* or we could just return the software copy of the output values if
547          * it was a purely digital output subdevice */
548         /* data[1]=s->state; */
549
550         return insn->n;
551 }
552
553 static int skel_dio_insn_config(struct comedi_device *dev,
554                                 struct comedi_subdevice *s,
555                                 struct comedi_insn *insn, unsigned int *data)
556 {
557         int chan = CR_CHAN(insn->chanspec);
558
559         /* The input or output configuration of each digital line is
560          * configured by a special insn_config instruction.  chanspec
561          * contains the channel to be changed, and data[0] contains the
562          * value COMEDI_INPUT or COMEDI_OUTPUT. */
563         switch (data[0]) {
564         case INSN_CONFIG_DIO_OUTPUT:
565                 s->io_bits |= 1 << chan;
566                 break;
567         case INSN_CONFIG_DIO_INPUT:
568                 s->io_bits &= ~(1 << chan);
569                 break;
570         case INSN_CONFIG_DIO_QUERY:
571                 data[1] =
572                     (s->io_bits & (1 << chan)) ? COMEDI_OUTPUT : COMEDI_INPUT;
573                 return insn->n;
574                 break;
575         default:
576                 return -EINVAL;
577                 break;
578         }
579         /* outw(s->io_bits,dev->iobase + SKEL_DIO_CONFIG); */
580
581         return insn->n;
582 }
583
584 #ifdef CONFIG_COMEDI_PCI_DRIVERS
585 static int __devinit driver_skel_pci_probe(struct pci_dev *dev,
586                                            const struct pci_device_id *ent)
587 {
588         return comedi_pci_auto_config(dev, &driver_skel);
589 }
590
591 static void __devexit driver_skel_pci_remove(struct pci_dev *dev)
592 {
593         comedi_pci_auto_unconfig(dev);
594 }
595
596 static struct pci_driver driver_skel_pci_driver = {
597         .id_table = skel_pci_table,
598         .probe = &driver_skel_pci_probe,
599         .remove = __devexit_p(&driver_skel_pci_remove)
600 };
601
602 static int __init driver_skel_init_module(void)
603 {
604         int retval;
605
606         retval = comedi_driver_register(&driver_skel);
607         if (retval < 0)
608                 return retval;
609
610         driver_skel_pci_driver.name = (char *)driver_skel.driver_name;
611         return pci_register_driver(&driver_skel_pci_driver);
612 }
613
614 static void __exit driver_skel_cleanup_module(void)
615 {
616         pci_unregister_driver(&driver_skel_pci_driver);
617         comedi_driver_unregister(&driver_skel);
618 }
619
620 module_init(driver_skel_init_module);
621 module_exit(driver_skel_cleanup_module);
622 #else
623 module_comedi_driver(driver_skel);
624 #endif
625
626 MODULE_AUTHOR("Comedi http://www.comedi.org");
627 MODULE_DESCRIPTION("Comedi low-level driver");
628 MODULE_LICENSE("GPL");