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()
107 #define comedi_rt_task_context_t int
110 #ifdef CONFIG_COMEDI_RTAI
112 #include <rtai_sched.h>
113 #include <rtai_version.h>
115 /* RTAI_VERSION_CODE doesn't work for rtai-3.6-cv and other strange versions.
116 * These are characterized by CONFIG_RTAI_REVISION_LEVEL being defined as an
117 * empty macro and CONFIG_RTAI_VERSION_MINOR being defined as something like
118 * '6-cv' or '7-test1'. The problem has been noted by the RTAI folks and they
119 * promise not to do it again. :-) Try and work around it here. */
120 #if !(CONFIG_RTAI_REVISION_LEVEL + 0)
121 #undef CONFIG_RTAI_REVISION_LEVEL
122 #define CONFIG_RTAI_REVISION_LEVEL 0
129 #if RTAI_VERSION_CODE < RTAI_MANGLE_VERSION(3, 3, 0)
130 #define comedi_rt_task_context_t int
132 #define comedi_rt_task_context_t long
135 /* Finished checking RTAI_VERSION_CODE. */
143 /* This defines the fastest speed we will emulate. Note that
144 * without a watchdog (like in RTAI), we could easily overrun our
145 * task period because analog input tends to be slow. */
146 #define SPEED_LIMIT 100000 /* in nanoseconds */
148 static int timer_attach(struct comedi_device
*dev
, struct comedi_devconfig
*it
);
149 static int timer_detach(struct comedi_device
*dev
);
150 static int timer_inttrig(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
151 unsigned int trig_num
);
152 static int timer_start_cmd(struct comedi_device
*dev
, struct comedi_subdevice
*s
);
154 static struct comedi_driver driver_timer
= {
155 .module
= THIS_MODULE
,
156 .driver_name
= "comedi_rt_timer",
157 .attach
= timer_attach
,
158 .detach
= timer_detach
,
159 /* .open = timer_open, */
162 COMEDI_INITCLEANUP(driver_timer
);
164 struct timer_private
{
165 comedi_t
*device
; /* device we are emulating commands for */
166 int subd
; /* subdevice we are emulating commands for */
167 RT_TASK
*rt_task
; /* rt task that starts scans */
168 RT_TASK
*scan_task
; /* rt task that controls conversion timing in a scan */
169 /* io_function can point to either an input or output function
170 * depending on what kind of subdevice we are emulating for */
171 int (*io_function
) (struct comedi_device
*dev
, struct comedi_cmd
*cmd
,
174 * RTIME has units of 1 = 838 nanoseconds time at which first scan
175 * started, used to check scan timing
178 /* time between scans */
180 /* time between conversions in a scan */
181 RTIME convert_period
;
183 volatile int stop
; /* indicates we should stop */
184 volatile int rt_task_active
; /* indicates rt_task is servicing a struct comedi_cmd */
185 volatile int scan_task_active
; /* indicates scan_task is servicing a struct comedi_cmd */
186 unsigned timer_running
:1;
188 #define devpriv ((struct timer_private *)dev->private)
190 static int timer_cancel(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
197 /* checks for scan timing error */
198 inline static int check_scan_timing(struct comedi_device
*dev
,
199 unsigned long long scan
)
201 RTIME now
, timing_error
;
204 timing_error
= now
- (devpriv
->start
+ scan
* devpriv
->scan_period
);
205 if (timing_error
> devpriv
->scan_period
) {
206 comedi_error(dev
, "timing error");
207 printk("scan started %i ns late\n", timing_error
* 838);
214 /* checks for conversion timing error */
215 inline static int check_conversion_timing(struct comedi_device
*dev
,
216 RTIME scan_start
, unsigned int conversion
)
218 RTIME now
, timing_error
;
222 now
- (scan_start
+ conversion
* devpriv
->convert_period
);
223 if (timing_error
> devpriv
->convert_period
) {
224 comedi_error(dev
, "timing error");
225 printk("conversion started %i ns late\n",
233 /* devpriv->io_function for an input subdevice */
234 static int timer_data_read(struct comedi_device
*dev
, struct comedi_cmd
*cmd
,
237 struct comedi_subdevice
*s
= dev
->read_subdev
;
241 ret
= comedi_data_read(devpriv
->device
, devpriv
->subd
,
242 CR_CHAN(cmd
->chanlist
[index
]),
243 CR_RANGE(cmd
->chanlist
[index
]),
244 CR_AREF(cmd
->chanlist
[index
]), &data
);
246 comedi_error(dev
, "read error");
249 if (s
->flags
& SDF_LSAMPL
) {
250 cfc_write_long_to_buffer(s
, data
);
252 comedi_buf_put(s
->async
, data
);
258 /* devpriv->io_function for an output subdevice */
259 static int timer_data_write(struct comedi_device
*dev
, struct comedi_cmd
*cmd
,
262 struct comedi_subdevice
*s
= dev
->write_subdev
;
263 unsigned int num_bytes
;
265 unsigned int long_data
;
268 if (s
->flags
& SDF_LSAMPL
) {
270 cfc_read_array_from_buffer(s
, &long_data
,
273 num_bytes
= cfc_read_array_from_buffer(s
, &data
, sizeof(data
));
277 if (num_bytes
== 0) {
278 comedi_error(dev
, "buffer underrun");
281 ret
= comedi_data_write(devpriv
->device
, devpriv
->subd
,
282 CR_CHAN(cmd
->chanlist
[index
]),
283 CR_RANGE(cmd
->chanlist
[index
]),
284 CR_AREF(cmd
->chanlist
[index
]), long_data
);
286 comedi_error(dev
, "write error");
293 /* devpriv->io_function for DIO subdevices */
294 static int timer_dio_read(struct comedi_device
*dev
, struct comedi_cmd
*cmd
,
297 struct comedi_subdevice
*s
= dev
->read_subdev
;
301 ret
= comedi_dio_bitfield(devpriv
->device
, devpriv
->subd
, 0, &data
);
303 comedi_error(dev
, "read error");
307 if (s
->flags
& SDF_LSAMPL
)
308 cfc_write_long_to_buffer(s
, data
);
310 cfc_write_to_buffer(s
, data
);
316 static void scan_task_func(comedi_rt_task_context_t d
)
318 struct comedi_device
*dev
= (struct comedi_device
*) d
;
319 struct comedi_subdevice
*s
= dev
->subdevices
+ 0;
320 struct comedi_async
*async
= s
->async
;
321 struct comedi_cmd
*cmd
= &async
->cmd
;
323 unsigned long long n
;
326 /* every struct comedi_cmd causes one execution of while loop */
328 devpriv
->scan_task_active
= 1;
329 /* each for loop completes one scan */
330 for (n
= 0; n
< cmd
->stop_arg
|| cmd
->stop_src
== TRIG_NONE
;
333 /* suspend task until next scan */
334 ret
= rt_task_suspend(devpriv
->scan_task
);
337 "error suspending scan task");
338 async
->events
|= COMEDI_CB_ERROR
;
342 /* check if stop flag was set (by timer_cancel()) */
345 ret
= check_scan_timing(dev
, n
);
347 async
->events
|= COMEDI_CB_ERROR
;
350 scan_start
= rt_get_time();
351 for (i
= 0; i
< cmd
->scan_end_arg
; i
++) {
352 /* conversion timing */
353 if (cmd
->convert_src
== TRIG_TIMER
&& i
) {
354 rt_task_wait_period();
355 ret
= check_conversion_timing(dev
,
363 ret
= devpriv
->io_function(dev
, cmd
, i
);
365 async
->events
|= COMEDI_CB_ERROR
;
369 s
->async
->events
|= COMEDI_CB_BLOCK
;
370 comedi_event(dev
, s
);
371 s
->async
->events
= 0;
376 comedi_unlock(devpriv
->device
, devpriv
->subd
);
377 async
->events
|= COMEDI_CB_EOA
;
378 comedi_event(dev
, s
);
380 devpriv
->scan_task_active
= 0;
381 /* suspend task until next struct comedi_cmd */
382 rt_task_suspend(devpriv
->scan_task
);
386 static void timer_task_func(comedi_rt_task_context_t d
)
388 struct comedi_device
*dev
= (struct comedi_device
*) d
;
389 struct comedi_subdevice
*s
= dev
->subdevices
+ 0;
390 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
392 unsigned long long n
;
394 /* every struct comedi_cmd causes one execution of while loop */
396 devpriv
->rt_task_active
= 1;
397 devpriv
->scan_task_active
= 1;
398 devpriv
->start
= rt_get_time();
400 for (n
= 0; n
< cmd
->stop_arg
|| cmd
->stop_src
== TRIG_NONE
;
404 rt_task_wait_period();
405 if (devpriv
->scan_task_active
== 0) {
408 ret
= rt_task_make_periodic(devpriv
->scan_task
,
409 devpriv
->start
+ devpriv
->scan_period
* n
,
410 devpriv
->convert_period
);
412 comedi_error(dev
, "bug!");
418 devpriv
->rt_task_active
= 0;
419 /* suspend until next struct comedi_cmd */
420 rt_task_suspend(devpriv
->rt_task
);
424 static int timer_insn(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
425 struct comedi_insn
*insn
, unsigned int *data
)
427 struct comedi_insn xinsn
= *insn
;
430 xinsn
.subdev
= devpriv
->subd
;
432 return comedi_do_insn(devpriv
->device
, &xinsn
);
435 static int cmdtest_helper(struct comedi_cmd
*cmd
,
436 unsigned int start_src
,
437 unsigned int scan_begin_src
,
438 unsigned int convert_src
,
439 unsigned int scan_end_src
, unsigned int stop_src
)
444 tmp
= cmd
->start_src
;
445 cmd
->start_src
&= start_src
;
446 if (!cmd
->start_src
|| tmp
!= cmd
->start_src
)
449 tmp
= cmd
->scan_begin_src
;
450 cmd
->scan_begin_src
&= scan_begin_src
;
451 if (!cmd
->scan_begin_src
|| tmp
!= cmd
->scan_begin_src
)
454 tmp
= cmd
->convert_src
;
455 cmd
->convert_src
&= convert_src
;
456 if (!cmd
->convert_src
|| tmp
!= cmd
->convert_src
)
459 tmp
= cmd
->scan_end_src
;
460 cmd
->scan_end_src
&= scan_end_src
;
461 if (!cmd
->scan_end_src
|| tmp
!= cmd
->scan_end_src
)
465 cmd
->stop_src
&= stop_src
;
466 if (!cmd
->stop_src
|| tmp
!= cmd
->stop_src
)
472 static int timer_cmdtest(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
473 struct comedi_cmd
*cmd
)
476 unsigned int start_src
= 0;
478 if (s
->type
== COMEDI_SUBD_AO
)
479 start_src
= TRIG_INT
;
481 start_src
= TRIG_NOW
;
483 err
= cmdtest_helper(cmd
, start_src
, /* start_src */
484 TRIG_TIMER
| TRIG_FOLLOW
, /* scan_begin_src */
485 TRIG_NOW
| TRIG_TIMER
, /* convert_src */
486 TRIG_COUNT
, /* scan_end_src */
487 TRIG_COUNT
| TRIG_NONE
); /* stop_src */
491 /* step 2: make sure trigger sources are unique and mutually
494 if (cmd
->start_src
!= TRIG_NOW
&& cmd
->start_src
!= TRIG_INT
)
496 if (cmd
->scan_begin_src
!= TRIG_TIMER
&&
497 cmd
->scan_begin_src
!= TRIG_FOLLOW
)
499 if (cmd
->convert_src
!= TRIG_TIMER
&& cmd
->convert_src
!= TRIG_NOW
)
501 if (cmd
->stop_src
!= TRIG_COUNT
&& cmd
->stop_src
!= TRIG_NONE
)
503 if (cmd
->scan_begin_src
== TRIG_FOLLOW
504 && cmd
->convert_src
!= TRIG_TIMER
)
506 if (cmd
->convert_src
== TRIG_NOW
&& cmd
->scan_begin_src
!= TRIG_TIMER
)
512 /* step 3: make sure arguments are trivially compatible */
513 /* limit frequency, this is fairly arbitrary */
514 if (cmd
->scan_begin_src
== TRIG_TIMER
) {
515 if (cmd
->scan_begin_arg
< SPEED_LIMIT
) {
516 cmd
->scan_begin_arg
= SPEED_LIMIT
;
520 if (cmd
->convert_src
== TRIG_TIMER
) {
521 if (cmd
->convert_arg
< SPEED_LIMIT
) {
522 cmd
->convert_arg
= SPEED_LIMIT
;
526 /* make sure conversion and scan frequencies are compatible */
527 if (cmd
->convert_src
== TRIG_TIMER
&& cmd
->scan_begin_src
== TRIG_TIMER
) {
528 if (cmd
->convert_arg
* cmd
->scan_end_arg
> cmd
->scan_begin_arg
) {
529 cmd
->scan_begin_arg
=
530 cmd
->convert_arg
* cmd
->scan_end_arg
;
537 /* step 4: fix up and arguments */
544 static int timer_cmd(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
547 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
549 /* hack attack: drivers are not supposed to do this: */
552 /* make sure tasks have finished cleanup of last struct comedi_cmd */
553 if (devpriv
->rt_task_active
|| devpriv
->scan_task_active
)
556 ret
= comedi_lock(devpriv
->device
, devpriv
->subd
);
558 comedi_error(dev
, "failed to obtain lock");
561 switch (cmd
->scan_begin_src
) {
563 devpriv
->scan_period
= nano2count(cmd
->scan_begin_arg
);
566 devpriv
->scan_period
=
567 nano2count(cmd
->convert_arg
* cmd
->scan_end_arg
);
570 comedi_error(dev
, "bug setting scan period!");
574 switch (cmd
->convert_src
) {
576 devpriv
->convert_period
= nano2count(cmd
->convert_arg
);
579 devpriv
->convert_period
= 1;
582 comedi_error(dev
, "bug setting conversion period!");
587 if (cmd
->start_src
== TRIG_NOW
)
588 return timer_start_cmd(dev
, s
);
590 s
->async
->inttrig
= timer_inttrig
;
595 static int timer_inttrig(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
596 unsigned int trig_num
)
601 s
->async
->inttrig
= NULL
;
603 return timer_start_cmd(dev
, s
);
606 static int timer_start_cmd(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
608 struct comedi_async
*async
= s
->async
;
609 struct comedi_cmd
*cmd
= &async
->cmd
;
610 RTIME now
, delay
, period
;
614 s
->async
->events
= 0;
616 if (cmd
->start_src
== TRIG_NOW
)
617 delay
= nano2count(cmd
->start_arg
);
622 /* Using 'period' this way gets around some weird bug in gcc-2.95.2
623 * that generates the compile error 'internal error--unrecognizable insn'
624 * when rt_task_make_period() is called (observed with rtlinux-3.1, linux-2.2.19).
626 period
= devpriv
->scan_period
;
627 ret
= rt_task_make_periodic(devpriv
->rt_task
, now
+ delay
, period
);
629 comedi_error(dev
, "error starting rt_task");
635 static int timer_attach(struct comedi_device
*dev
, struct comedi_devconfig
*it
)
638 struct comedi_subdevice
*s
, *emul_s
;
639 struct comedi_device
*emul_dev
;
640 /* These should probably be devconfig options[] */
641 const int timer_priority
= 4;
642 const int scan_priority
= timer_priority
+ 1;
645 printk("comedi%d: timer: ", dev
->minor
);
647 dev
->board_name
= "timer";
649 ret
= alloc_subdevices(dev
, 1);
653 ret
= alloc_private(dev
, sizeof(struct timer_private
));
657 sprintf(path
, "/dev/comedi%d", it
->options
[0]);
658 devpriv
->device
= comedi_open(path
);
659 devpriv
->subd
= it
->options
[1];
661 printk("emulating commands for minor %i, subdevice %d\n",
662 it
->options
[0], devpriv
->subd
);
664 emul_dev
= devpriv
->device
;
665 emul_s
= emul_dev
->subdevices
+ devpriv
->subd
;
667 /* input or output subdevice */
668 s
= dev
->subdevices
+ 0;
669 s
->type
= emul_s
->type
;
670 s
->subdev_flags
= emul_s
->subdev_flags
; /* SDF_GROUND (to fool check_driver) */
671 s
->n_chan
= emul_s
->n_chan
;
672 s
->len_chanlist
= 1024;
673 s
->do_cmd
= timer_cmd
;
674 s
->do_cmdtest
= timer_cmdtest
;
675 s
->cancel
= timer_cancel
;
676 s
->maxdata
= emul_s
->maxdata
;
677 s
->range_table
= emul_s
->range_table
;
678 s
->range_table_list
= emul_s
->range_table_list
;
679 switch (emul_s
->type
) {
681 s
->insn_read
= timer_insn
;
682 dev
->read_subdev
= s
;
683 s
->subdev_flags
|= SDF_CMD_READ
;
684 devpriv
->io_function
= timer_data_read
;
687 s
->insn_write
= timer_insn
;
688 s
->insn_read
= timer_insn
;
689 dev
->write_subdev
= s
;
690 s
->subdev_flags
|= SDF_CMD_WRITE
;
691 devpriv
->io_function
= timer_data_write
;
693 case COMEDI_SUBD_DIO
:
694 s
->insn_write
= timer_insn
;
695 s
->insn_read
= timer_insn
;
696 s
->insn_bits
= timer_insn
;
697 dev
->read_subdev
= s
;
698 s
->subdev_flags
|= SDF_CMD_READ
;
699 devpriv
->io_function
= timer_dio_read
;
702 comedi_error(dev
, "failed to determine subdevice type!");
706 rt_set_oneshot_mode();
708 devpriv
->timer_running
= 1;
710 devpriv
->rt_task
= kzalloc(sizeof(RT_TASK
), GFP_KERNEL
);
712 /* initialize real-time tasks */
713 ret
= rt_task_init(devpriv
->rt_task
, timer_task_func
,
714 (comedi_rt_task_context_t
) dev
, 3000, timer_priority
, 0, 0);
716 comedi_error(dev
, "error initalizing rt_task");
717 kfree(devpriv
->rt_task
);
718 devpriv
->rt_task
= 0;
722 devpriv
->scan_task
= kzalloc(sizeof(RT_TASK
), GFP_KERNEL
);
724 ret
= rt_task_init(devpriv
->scan_task
, scan_task_func
,
725 (comedi_rt_task_context_t
) dev
, 3000, scan_priority
, 0, 0);
727 comedi_error(dev
, "error initalizing scan_task");
728 kfree(devpriv
->scan_task
);
729 devpriv
->scan_task
= 0;
736 /* free allocated resources */
737 static int timer_detach(struct comedi_device
*dev
)
739 printk("comedi%d: timer: remove\n", dev
->minor
);
742 if (devpriv
->rt_task
) {
743 rt_task_delete(devpriv
->rt_task
);
744 kfree(devpriv
->rt_task
);
746 if (devpriv
->scan_task
) {
747 rt_task_delete(devpriv
->scan_task
);
748 kfree(devpriv
->scan_task
);
750 if (devpriv
->timer_running
)
753 comedi_close(devpriv
->device
);