2 comedi/drivers/comedi_rt_timer.c
3 virtual driver for using RTL timing sources
5 Authors: David A. Schleef, Frank M. Hess
7 COMEDI - Linux Control and Measurement Device Interface
8 Copyright (C) 1999,2001 David A. Schleef <ds@schleef.org>
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 **************************************************************************
27 Driver: comedi_rt_timer
28 Description: Command emulator using real-time tasks
33 This driver requires RTAI or RTLinux to work correctly. It doesn't
34 actually drive hardware directly, but calls other drivers and uses
35 a real-time task to emulate commands for drivers and devices that
36 are incapable of native commands. Thus, you can get accurately
37 timed I/O on any device.
39 Since the timing is all done in software, sampling jitter is much
40 higher than with a device that has an on-board timer, and maximum
41 sample rate is much lower.
43 Configuration options:
44 [0] - minor number of device you wish to emulate commands for
45 [1] - subdevice number you wish to emulate commands for
49 Support for digital io commands could be added, except I can't see why
50 anyone would want to use them
51 What happens if device we are emulating for is de-configured?
54 #include "../comedidev.h"
55 #include "../comedilib.h"
57 #include "comedi_fc.h"
59 #ifdef CONFIG_COMEDI_RTL_V1
60 #include <rtl_sched.h>
61 #include <asm/rt_irq.h>
63 #ifdef CONFIG_COMEDI_RTL
65 #include <rtl_sched.h>
66 #include <rtl_compat.h>
67 #include <asm/div64.h>
69 #ifndef RTLINUX_VERSION_CODE
70 #define RTLINUX_VERSION_CODE 0
72 #ifndef RTLINUX_VERSION
73 #define RTLINUX_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
76 // begin hack to workaround broken HRT_TO_8254() function on rtlinux
77 #if RTLINUX_VERSION_CODE <= RTLINUX_VERSION(3,0,100)
78 // this function sole purpose is to divide a long long by 838
79 static inline RTIME nano2count(long long ns)
88 #define rt_get_time() nano2count(gethrtime())
92 #define nano2count(x) HRT_TO_8254(x)
96 // rtl-rtai compatibility
97 #define rt_task_wait_period() rt_task_wait()
98 #define rt_pend_linux_srq(irq) rtl_global_pend_irq(irq)
99 #define rt_free_srq(irq) rtl_free_soft_irq(irq)
100 #define rt_request_srq(x,y,z) rtl_get_soft_irq(y,"timer")
101 #define rt_task_init(a,b,c,d,e,f,g) rt_task_init(a,b,c,d,(e)+1)
102 #define rt_task_resume(x) rt_task_wakeup(x)
103 #define rt_set_oneshot_mode()
104 #define start_rt_timer(x)
105 #define stop_rt_timer()
108 #ifdef CONFIG_COMEDI_RTAI
110 #include <rtai_sched.h>
112 #if RTAI_VERSION_CODE < RTAI_MANGLE_VERSION(3,3,0)
113 #define comedi_rt_task_context_t int
115 #define comedi_rt_task_context_t long
120 /* This defines the fastest speed we will emulate. Note that
121 * without a watchdog (like in RTAI), we could easily overrun our
122 * task period because analog input tends to be slow. */
123 #define SPEED_LIMIT 100000 /* in nanoseconds */
125 static int timer_attach(struct comedi_device * dev, comedi_devconfig * it);
126 static int timer_detach(struct comedi_device * dev);
127 static int timer_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
128 unsigned int trig_num);
129 static int timer_start_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
131 static struct comedi_driver driver_timer = {
133 driver_name:"comedi_rt_timer",
139 COMEDI_INITCLEANUP(driver_timer);
142 comedi_t *device; // device we are emulating commands for
143 int subd; // subdevice we are emulating commands for
144 RT_TASK *rt_task; // rt task that starts scans
145 RT_TASK *scan_task; // rt task that controls conversion timing in a scan
146 /* io_function can point to either an input or output function
147 * depending on what kind of subdevice we are emulating for */
148 int (*io_function) (struct comedi_device * dev, struct comedi_cmd * cmd,
150 // RTIME has units of 1 = 838 nanoseconds
151 // time at which first scan started, used to check scan timing
153 // time between scans
155 // time between conversions in a scan
156 RTIME convert_period;
158 volatile int stop; // indicates we should stop
159 volatile int rt_task_active; // indicates rt_task is servicing a struct comedi_cmd
160 volatile int scan_task_active; // indicates scan_task is servicing a struct comedi_cmd
161 unsigned timer_running:1;
163 #define devpriv ((timer_private *)dev->private)
165 static int timer_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
172 // checks for scan timing error
173 inline static int check_scan_timing(struct comedi_device * dev,
174 unsigned long long scan)
176 RTIME now, timing_error;
179 timing_error = now - (devpriv->start + scan * devpriv->scan_period);
180 if (timing_error > devpriv->scan_period) {
181 comedi_error(dev, "timing error");
182 rt_printk("scan started %i ns late\n", timing_error * 838);
189 // checks for conversion timing error
190 inline static int check_conversion_timing(struct comedi_device * dev,
191 RTIME scan_start, unsigned int conversion)
193 RTIME now, timing_error;
197 now - (scan_start + conversion * devpriv->convert_period);
198 if (timing_error > devpriv->convert_period) {
199 comedi_error(dev, "timing error");
200 rt_printk("conversion started %i ns late\n",
208 // devpriv->io_function for an input subdevice
209 static int timer_data_read(struct comedi_device * dev, struct comedi_cmd * cmd,
212 struct comedi_subdevice *s = dev->read_subdev;
216 ret = comedi_data_read(devpriv->device, devpriv->subd,
217 CR_CHAN(cmd->chanlist[index]),
218 CR_RANGE(cmd->chanlist[index]),
219 CR_AREF(cmd->chanlist[index]), &data);
221 comedi_error(dev, "read error");
224 if (s->flags & SDF_LSAMPL) {
225 cfc_write_long_to_buffer(s, data);
227 comedi_buf_put(s->async, data);
233 // devpriv->io_function for an output subdevice
234 static int timer_data_write(struct comedi_device * dev, struct comedi_cmd * cmd,
237 struct comedi_subdevice *s = dev->write_subdev;
238 unsigned int num_bytes;
240 unsigned int long_data;
243 if (s->flags & SDF_LSAMPL) {
245 cfc_read_array_from_buffer(s, &long_data,
248 num_bytes = cfc_read_array_from_buffer(s, &data, sizeof(data));
252 if (num_bytes == 0) {
253 comedi_error(dev, "buffer underrun");
256 ret = comedi_data_write(devpriv->device, devpriv->subd,
257 CR_CHAN(cmd->chanlist[index]),
258 CR_RANGE(cmd->chanlist[index]),
259 CR_AREF(cmd->chanlist[index]), long_data);
261 comedi_error(dev, "write error");
268 // devpriv->io_function for DIO subdevices
269 static int timer_dio_read(struct comedi_device * dev, struct comedi_cmd * cmd,
272 struct comedi_subdevice *s = dev->read_subdev;
276 ret = comedi_dio_bitfield(devpriv->device, devpriv->subd, 0, &data);
278 comedi_error(dev, "read error");
282 if (s->flags & SDF_LSAMPL)
283 cfc_write_long_to_buffer(s, data);
285 cfc_write_to_buffer(s, data);
291 static void scan_task_func(comedi_rt_task_context_t d)
293 struct comedi_device *dev = (struct comedi_device *) d;
294 struct comedi_subdevice *s = dev->subdevices + 0;
295 struct comedi_async *async = s->async;
296 struct comedi_cmd *cmd = &async->cmd;
298 unsigned long long n;
301 // every struct comedi_cmd causes one execution of while loop
303 devpriv->scan_task_active = 1;
304 // each for loop completes one scan
305 for (n = 0; n < cmd->stop_arg || cmd->stop_src == TRIG_NONE;
308 // suspend task until next scan
309 ret = rt_task_suspend(devpriv->scan_task);
312 "error suspending scan task");
313 async->events |= COMEDI_CB_ERROR;
317 // check if stop flag was set (by timer_cancel())
320 ret = check_scan_timing(dev, n);
322 async->events |= COMEDI_CB_ERROR;
325 scan_start = rt_get_time();
326 for (i = 0; i < cmd->scan_end_arg; i++) {
328 if (cmd->convert_src == TRIG_TIMER && i) {
329 rt_task_wait_period();
330 ret = check_conversion_timing(dev,
338 ret = devpriv->io_function(dev, cmd, i);
340 async->events |= COMEDI_CB_ERROR;
344 s->async->events |= COMEDI_CB_BLOCK;
345 comedi_event(dev, s);
346 s->async->events = 0;
351 comedi_unlock(devpriv->device, devpriv->subd);
352 async->events |= COMEDI_CB_EOA;
353 comedi_event(dev, s);
355 devpriv->scan_task_active = 0;
356 // suspend task until next struct comedi_cmd
357 rt_task_suspend(devpriv->scan_task);
361 static void timer_task_func(comedi_rt_task_context_t d)
363 struct comedi_device *dev = (struct comedi_device *) d;
364 struct comedi_subdevice *s = dev->subdevices + 0;
365 struct comedi_cmd *cmd = &s->async->cmd;
367 unsigned long long n;
369 // every struct comedi_cmd causes one execution of while loop
371 devpriv->rt_task_active = 1;
372 devpriv->scan_task_active = 1;
373 devpriv->start = rt_get_time();
375 for (n = 0; n < cmd->stop_arg || cmd->stop_src == TRIG_NONE;
379 rt_task_wait_period();
380 if (devpriv->scan_task_active == 0) {
383 ret = rt_task_make_periodic(devpriv->scan_task,
384 devpriv->start + devpriv->scan_period * n,
385 devpriv->convert_period);
387 comedi_error(dev, "bug!");
393 devpriv->rt_task_active = 0;
394 // suspend until next struct comedi_cmd
395 rt_task_suspend(devpriv->rt_task);
399 static int timer_insn(struct comedi_device * dev, struct comedi_subdevice * s,
400 comedi_insn * insn, unsigned int * data)
402 comedi_insn xinsn = *insn;
405 xinsn.subdev = devpriv->subd;
407 return comedi_do_insn(devpriv->device, &xinsn);
410 static int cmdtest_helper(struct comedi_cmd * cmd,
411 unsigned int start_src,
412 unsigned int scan_begin_src,
413 unsigned int convert_src,
414 unsigned int scan_end_src, unsigned int stop_src)
419 tmp = cmd->start_src;
420 cmd->start_src &= start_src;
421 if (!cmd->start_src || tmp != cmd->start_src)
424 tmp = cmd->scan_begin_src;
425 cmd->scan_begin_src &= scan_begin_src;
426 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
429 tmp = cmd->convert_src;
430 cmd->convert_src &= convert_src;
431 if (!cmd->convert_src || tmp != cmd->convert_src)
434 tmp = cmd->scan_end_src;
435 cmd->scan_end_src &= scan_end_src;
436 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
440 cmd->stop_src &= stop_src;
441 if (!cmd->stop_src || tmp != cmd->stop_src)
447 static int timer_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
448 struct comedi_cmd * cmd)
451 unsigned int start_src = 0;
453 if (s->type == COMEDI_SUBD_AO)
454 start_src = TRIG_INT;
456 start_src = TRIG_NOW;
458 err = cmdtest_helper(cmd, start_src, /* start_src */
459 TRIG_TIMER | TRIG_FOLLOW, /* scan_begin_src */
460 TRIG_NOW | TRIG_TIMER, /* convert_src */
461 TRIG_COUNT, /* scan_end_src */
462 TRIG_COUNT | TRIG_NONE); /* stop_src */
466 /* step 2: make sure trigger sources are unique and mutually
469 if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_INT)
471 if (cmd->scan_begin_src != TRIG_TIMER &&
472 cmd->scan_begin_src != TRIG_FOLLOW)
474 if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_NOW)
476 if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
478 if (cmd->scan_begin_src == TRIG_FOLLOW
479 && cmd->convert_src != TRIG_TIMER)
481 if (cmd->convert_src == TRIG_NOW && cmd->scan_begin_src != TRIG_TIMER)
487 /* step 3: make sure arguments are trivially compatible */
488 // limit frequency, this is fairly arbitrary
489 if (cmd->scan_begin_src == TRIG_TIMER) {
490 if (cmd->scan_begin_arg < SPEED_LIMIT) {
491 cmd->scan_begin_arg = SPEED_LIMIT;
495 if (cmd->convert_src == TRIG_TIMER) {
496 if (cmd->convert_arg < SPEED_LIMIT) {
497 cmd->convert_arg = SPEED_LIMIT;
501 // make sure conversion and scan frequencies are compatible
502 if (cmd->convert_src == TRIG_TIMER && cmd->scan_begin_src == TRIG_TIMER) {
503 if (cmd->convert_arg * cmd->scan_end_arg > cmd->scan_begin_arg) {
504 cmd->scan_begin_arg =
505 cmd->convert_arg * cmd->scan_end_arg;
512 /* step 4: fix up and arguments */
519 static int timer_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
522 struct comedi_cmd *cmd = &s->async->cmd;
524 /* hack attack: drivers are not supposed to do this: */
527 // make sure tasks have finished cleanup of last struct comedi_cmd
528 if (devpriv->rt_task_active || devpriv->scan_task_active)
531 ret = comedi_lock(devpriv->device, devpriv->subd);
533 comedi_error(dev, "failed to obtain lock");
536 switch (cmd->scan_begin_src) {
538 devpriv->scan_period = nano2count(cmd->scan_begin_arg);
541 devpriv->scan_period =
542 nano2count(cmd->convert_arg * cmd->scan_end_arg);
545 comedi_error(dev, "bug setting scan period!");
549 switch (cmd->convert_src) {
551 devpriv->convert_period = nano2count(cmd->convert_arg);
554 devpriv->convert_period = 1;
557 comedi_error(dev, "bug setting conversion period!");
562 if (cmd->start_src == TRIG_NOW)
563 return timer_start_cmd(dev, s);
565 s->async->inttrig = timer_inttrig;
570 static int timer_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
571 unsigned int trig_num)
576 s->async->inttrig = NULL;
578 return timer_start_cmd(dev, s);
581 static int timer_start_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
583 struct comedi_async *async = s->async;
584 struct comedi_cmd *cmd = &async->cmd;
585 RTIME now, delay, period;
589 s->async->events = 0;
591 if (cmd->start_src == TRIG_NOW)
592 delay = nano2count(cmd->start_arg);
597 /* Using 'period' this way gets around some weird bug in gcc-2.95.2
598 * that generates the compile error 'internal error--unrecognizable insn'
599 * when rt_task_make_period() is called (observed with rtlinux-3.1, linux-2.2.19).
601 period = devpriv->scan_period;
602 ret = rt_task_make_periodic(devpriv->rt_task, now + delay, period);
604 comedi_error(dev, "error starting rt_task");
610 static int timer_attach(struct comedi_device * dev, comedi_devconfig * it)
613 struct comedi_subdevice *s, *emul_s;
614 struct comedi_device *emul_dev;
615 /* These should probably be devconfig options[] */
616 const int timer_priority = 4;
617 const int scan_priority = timer_priority + 1;
620 printk("comedi%d: timer: ", dev->minor);
622 dev->board_name = "timer";
624 if ((ret = alloc_subdevices(dev, 1)) < 0)
626 if ((ret = alloc_private(dev, sizeof(timer_private))) < 0)
629 sprintf(path, "/dev/comedi%d", it->options[0]);
630 devpriv->device = comedi_open(path);
631 devpriv->subd = it->options[1];
633 printk("emulating commands for minor %i, subdevice %d\n",
634 it->options[0], devpriv->subd);
636 emul_dev = devpriv->device;
637 emul_s = emul_dev->subdevices + devpriv->subd;
639 // input or output subdevice
640 s = dev->subdevices + 0;
641 s->type = emul_s->type;
642 s->subdev_flags = emul_s->subdev_flags; /* SDF_GROUND (to fool check_driver) */
643 s->n_chan = emul_s->n_chan;
644 s->len_chanlist = 1024;
645 s->do_cmd = timer_cmd;
646 s->do_cmdtest = timer_cmdtest;
647 s->cancel = timer_cancel;
648 s->maxdata = emul_s->maxdata;
649 s->range_table = emul_s->range_table;
650 s->range_table_list = emul_s->range_table_list;
651 switch (emul_s->type) {
653 s->insn_read = timer_insn;
654 dev->read_subdev = s;
655 s->subdev_flags |= SDF_CMD_READ;
656 devpriv->io_function = timer_data_read;
659 s->insn_write = timer_insn;
660 s->insn_read = timer_insn;
661 dev->write_subdev = s;
662 s->subdev_flags |= SDF_CMD_WRITE;
663 devpriv->io_function = timer_data_write;
665 case COMEDI_SUBD_DIO:
666 s->insn_write = timer_insn;
667 s->insn_read = timer_insn;
668 s->insn_bits = timer_insn;
669 dev->read_subdev = s;
670 s->subdev_flags |= SDF_CMD_READ;
671 devpriv->io_function = timer_dio_read;
674 comedi_error(dev, "failed to determine subdevice type!");
678 rt_set_oneshot_mode();
680 devpriv->timer_running = 1;
682 devpriv->rt_task = kzalloc(sizeof(RT_TASK), GFP_KERNEL);
684 // initialize real-time tasks
685 ret = rt_task_init(devpriv->rt_task, timer_task_func,
686 (comedi_rt_task_context_t) dev, 3000, timer_priority, 0, 0);
688 comedi_error(dev, "error initalizing rt_task");
689 kfree(devpriv->rt_task);
690 devpriv->rt_task = 0;
694 devpriv->scan_task = kzalloc(sizeof(RT_TASK), GFP_KERNEL);
696 ret = rt_task_init(devpriv->scan_task, scan_task_func,
697 (comedi_rt_task_context_t) dev, 3000, scan_priority, 0, 0);
699 comedi_error(dev, "error initalizing scan_task");
700 kfree(devpriv->scan_task);
701 devpriv->scan_task = 0;
708 // free allocated resources
709 static int timer_detach(struct comedi_device * dev)
711 printk("comedi%d: timer: remove\n", dev->minor);
714 if (devpriv->rt_task) {
715 rt_task_delete(devpriv->rt_task);
716 kfree(devpriv->rt_task);
718 if (devpriv->scan_task) {
719 rt_task_delete(devpriv->scan_task);
720 kfree(devpriv->scan_task);
722 if (devpriv->timer_running)
725 comedi_close(devpriv->device);