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