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