2 module/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.
25 **************************************************************************
28 Driver: comedi_rt_timer.o
29 Description: Command emulator using real-time tasks
34 This driver requires RTAI or RTLinux to work correctly. It doesn't
35 actually drive hardware directly, but calls other drivers and uses
36 a real-time task to emulate commands for drivers and devices that
37 are incapable of native commands. Thus, you can get accurately
38 timed I/O on any device.
40 Since the timing is all done in software, sampling jitter is much
41 higher than with a device that has an on-board timer, and maximum
42 sample rate is much lower.
44 Configuration options:
45 [0] - minor number of device you wish to emulate commands for
46 [1] - subdevice number you wish to emulate commands for
50 Support for digital io commands could be added, except I can't see why
51 anyone would want to use them
52 What happens if device we are emulating for is de-configured?
56 #include <linux/kernel.h>
57 #include <linux/module.h>
59 #include <linux/comedidev.h>
60 #include <linux/comedilib.h>
61 #ifdef CONFIG_COMEDI_RTL_V1
62 #include <rtl_sched.h>
63 #include <asm/rt_irq.h>
65 #ifdef CONFIG_COMEDI_RTL
67 #include <rtl_sched.h>
68 #include <rtl_compat.h>
69 #include <asm/div64.h>
71 #ifndef RTLINUX_VERSION_CODE
72 #define RTLINUX_VERSION_CODE 0
74 #ifndef RTLINUX_VERSION
75 #define RTLINUX_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
78 // begin hack to workaround broken HRT_TO_8254() function on rtlinux
79 #if RTLINUX_VERSION_CODE <= RTLINUX_VERSION(3,0,100)
80 // this function sole purpose is to divide a long long by 838
81 static inline RTIME nano2count(long long ns)
89 #define rt_get_time() nano2count(gethrtime())
93 #define nano2count(x) HRT_TO_8254(x)
97 // rtl-rtai compatibility
98 #define rt_task_wait_period() rt_task_wait()
99 #define rt_pend_linux_srq(irq) rtl_global_pend_irq(irq)
100 #define rt_free_srq(irq) rtl_free_soft_irq(irq)
101 #define rt_request_srq(x,y,z) rtl_get_soft_irq(y,"timer")
102 #define rt_task_init(a,b,c,d,e,f,g) rt_task_init(a,b,c,d,(e)+1)
103 #define rt_task_resume(x) rt_task_wakeup(x)
104 #define rt_set_oneshot_mode()
105 #define start_rt_timer(x)
106 #define stop_rt_timer()
109 #ifdef CONFIG_COMEDI_RTAI
111 #include <rtai_sched.h>
115 /* This defines the fastest speed we will emulate. Note that
116 * without a watchdog (like in RTAI), we could easily overrun our
117 * task period because analog input tends to be slow. */
118 #define SPEED_LIMIT 100000 /* in nanoseconds */
120 static int timer_attach(comedi_device *dev,comedi_devconfig *it);
121 static int timer_detach(comedi_device *dev);
122 static int timer_inttrig(comedi_device *dev, comedi_subdevice *s, unsigned int trig_num);
123 static int timer_start_cmd(comedi_device *dev, comedi_subdevice *s);
125 static comedi_driver driver_timer={
127 driver_name: "comedi_rt_timer",
128 attach: timer_attach,
129 detach: timer_detach,
131 COMEDI_INITCLEANUP(driver_timer);
135 comedi_t *device; // device we are emulating commands for
136 int subd; // subdevice we are emulating commands for
137 RT_TASK *rt_task; // rt task that starts scans
138 RT_TASK *scan_task; // rt task that controls conversion timing in a scan
139 /* io_function can point to either an input or output function
140 * depending on what kind of subdevice we are emulating for */
141 int (*io_function)(comedi_device *dev, comedi_cmd *cmd, unsigned int index);
142 // RTIME has units of 1 = 838 nanoseconds
143 // time at which first scan started, used to check scan timing
145 // time between scans
147 // time between conversions in a scan
148 RTIME convert_period;
150 volatile int stop; // indicates we should stop
151 volatile int rt_task_active; // indicates rt_task is servicing a comedi_cmd
152 volatile int scan_task_active; // indicates scan_task is servicing a comedi_cmd
153 unsigned timer_running : 1;
155 #define devpriv ((timer_private *)dev->private)
157 static int timer_cancel(comedi_device *dev,comedi_subdevice *s)
164 // checks for scan timing error
165 inline static int check_scan_timing(comedi_device *dev,
166 unsigned long long scan)
168 RTIME now, timing_error;
171 timing_error = now - (devpriv->start + scan * devpriv->scan_period);
172 if(timing_error > devpriv->scan_period){
173 comedi_error(dev, "timing error");
174 rt_printk("scan started %i ns late\n", timing_error * 838);
181 // checks for conversion timing error
182 inline static int check_conversion_timing(comedi_device *dev,
183 RTIME scan_start, unsigned int conversion)
185 RTIME now, timing_error;
188 timing_error = now - (scan_start + conversion * devpriv->convert_period);
189 if(timing_error > devpriv->convert_period){
190 comedi_error(dev, "timing error");
191 rt_printk("conversion started %i ns late\n", timing_error * 838);
198 // devpriv->io_function for an input subdevice
199 static int timer_data_read(comedi_device *dev, comedi_cmd *cmd,
202 comedi_subdevice *s = dev->read_subdev;
206 ret = comedi_data_read(devpriv->device, devpriv->subd,
207 CR_CHAN(cmd->chanlist[index]),
208 CR_RANGE(cmd->chanlist[index]),
209 CR_AREF(cmd->chanlist[index]),
212 comedi_error(dev, "read error");
215 if( s->flags % SDF_LSAMPL )
216 cfc_write_long_to_buffer( s, data );
218 cfc_write_to_buffer( s, data );
223 // devpriv->io_function for an output subdevice
224 static int timer_data_write(comedi_device *dev, comedi_cmd *cmd,
227 comedi_subdevice *s = dev->write_subdev;
228 unsigned int num_bytes;
232 if( s->flags & SDF_LSAMPL )
234 num_bytes = cfc_read_array_from_buffer( s, &long_data, sizeof( long_data ) );
236 num_bytes = cfc_read_array_from_buffer( s, &data, sizeof( data ) );
242 comedi_error(dev, "buffer underrun");
245 ret = comedi_data_write(devpriv->device, devpriv->subd,
246 CR_CHAN(cmd->chanlist[index]),
247 CR_RANGE(cmd->chanlist[index]),
248 CR_AREF(cmd->chanlist[index]),
251 comedi_error(dev, "write error");
258 // devpriv->io_function for DIO subdevices
259 static int timer_dio_read(comedi_device *dev, comedi_cmd *cmd,
262 comedi_subdevice *s = dev->read_subdev;
266 ret = comedi_dio_bitfield(devpriv->device, devpriv->subd, 0, &data);
268 comedi_error(dev, "read error");
272 if( s->flags % SDF_LSAMPL )
273 cfc_write_long_to_buffer( s, data );
275 cfc_write_to_buffer( s, data );
281 static void scan_task_func(int d)
283 comedi_device *dev=(comedi_device *)d;
284 comedi_subdevice *s = dev->subdevices + 0;
285 comedi_async *async = s->async;
286 comedi_cmd *cmd = &async->cmd;
288 unsigned long long n;
291 // every comedi_cmd causes one execution of while loop
293 devpriv->scan_task_active = 1;
294 // each for loop completes one scan
295 for(n = 0; n < cmd->stop_arg || cmd->stop_src == TRIG_NONE; n++){
297 // suspend task until next scan
298 ret = rt_task_suspend(devpriv->scan_task);
300 comedi_error(dev, "error suspending scan task");
301 async->events |= COMEDI_CB_ERROR;
305 // check if stop flag was set (by timer_cancel())
308 ret = check_scan_timing(dev, n);
310 async->events |= COMEDI_CB_ERROR;
313 scan_start = rt_get_time();
314 for(i = 0; i < cmd->scan_end_arg; i++){
316 if(cmd->convert_src == TRIG_TIMER && i){
317 rt_task_wait_period();
318 ret = check_conversion_timing(dev, scan_start, i);
320 async->events |= COMEDI_CB_ERROR;
324 ret = devpriv->io_function(dev, cmd, i);
326 async->events |= COMEDI_CB_ERROR;
330 s->async->events |= COMEDI_CB_BLOCK;
331 comedi_event(dev,s,s->async->events);
332 s->async->events = 0;
337 comedi_unlock(devpriv->device,devpriv->subd);
338 async->events |= COMEDI_CB_EOA;
339 comedi_event(dev, s, async->events);
341 devpriv->scan_task_active = 0;
342 // suspend task until next comedi_cmd
343 rt_task_suspend(devpriv->scan_task);
347 static void timer_task_func(int d)
349 comedi_device *dev=(comedi_device *)d;
350 comedi_subdevice *s = dev->subdevices + 0;
351 comedi_cmd *cmd=&s->async->cmd;
353 unsigned long long n;
355 // every comedi_cmd causes one execution of while loop
357 devpriv->rt_task_active = 1;
358 devpriv->scan_task_active = 1;
359 devpriv->start = rt_get_time();
361 for(n = 0; n < cmd->stop_arg || cmd->stop_src == TRIG_NONE; n++){
364 rt_task_wait_period();
365 if(devpriv->scan_task_active == 0){
368 ret = rt_task_make_periodic(devpriv->scan_task,
369 devpriv->start + devpriv->scan_period * n,
370 devpriv->convert_period);
372 comedi_error(dev, "bug!");
378 devpriv->rt_task_active = 0;
379 // suspend until next comedi_cmd
380 rt_task_suspend(devpriv->rt_task);
384 static int timer_insn(comedi_device *dev,comedi_subdevice *s,
385 comedi_insn *insn,lsampl_t *data)
387 comedi_insn xinsn = *insn;
390 xinsn.subdev = devpriv->subd;
392 return comedi_do_insn(devpriv->device,&xinsn);
395 static int cmdtest_helper(comedi_cmd *cmd,
396 unsigned int start_src,
397 unsigned int scan_begin_src,
398 unsigned int convert_src,
399 unsigned int scan_end_src,
400 unsigned int stop_src)
405 tmp = cmd->start_src;
406 cmd->start_src &= start_src;
407 if(!cmd->start_src || tmp!=cmd->start_src)err++;
409 tmp = cmd->scan_begin_src;
410 cmd->scan_begin_src &= scan_begin_src;
411 if(!cmd->scan_begin_src || tmp!=cmd->scan_begin_src)err++;
413 tmp = cmd->convert_src;
414 cmd->convert_src &= convert_src;
415 if(!cmd->convert_src || tmp!=cmd->convert_src)err++;
417 tmp = cmd->scan_end_src;
418 cmd->scan_end_src &= scan_end_src;
419 if(!cmd->scan_end_src || tmp!=cmd->scan_end_src)err++;
422 cmd->stop_src &= stop_src;
423 if(!cmd->stop_src || tmp!=cmd->stop_src)err++;
428 static int timer_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd)
431 unsigned int start_src = 0;
433 if(s->type == COMEDI_SUBD_AO)
434 start_src = TRIG_INT;
436 start_src = TRIG_NOW;
438 err = cmdtest_helper(cmd,
439 start_src, /* start_src */
440 TRIG_TIMER | TRIG_FOLLOW, /* scan_begin_src */
441 TRIG_NOW | TRIG_TIMER, /* convert_src */
442 TRIG_COUNT, /* scan_end_src */
443 TRIG_COUNT | TRIG_NONE); /* stop_src */
446 /* step 2: make sure trigger sources are unique and mutually
449 if(cmd->start_src != TRIG_NOW &&
450 cmd->start_src != TRIG_INT)
452 if(cmd->scan_begin_src != TRIG_TIMER &&
453 cmd->scan_begin_src != TRIG_FOLLOW)
455 if(cmd->convert_src != TRIG_TIMER &&
456 cmd->convert_src != TRIG_NOW)
458 if(cmd->stop_src != TRIG_COUNT &&
459 cmd->stop_src != TRIG_NONE)
461 if(cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src != TRIG_TIMER)
463 if(cmd->convert_src == TRIG_NOW && cmd->scan_begin_src != TRIG_TIMER)
468 /* step 3: make sure arguments are trivially compatible */
469 // limit frequency, this is fairly arbitrary
470 if(cmd->scan_begin_src == TRIG_TIMER){
471 if(cmd->scan_begin_arg<SPEED_LIMIT){
472 cmd->scan_begin_arg=SPEED_LIMIT;
476 if(cmd->convert_src == TRIG_TIMER){
477 if(cmd->convert_arg<SPEED_LIMIT){
478 cmd->convert_arg=SPEED_LIMIT;
482 // make sure conversion and scan frequencies are compatible
483 if(cmd->convert_src == TRIG_TIMER && cmd->scan_begin_src == TRIG_TIMER){
484 if(cmd->convert_arg * cmd->scan_end_arg > cmd->scan_begin_arg){
485 cmd->scan_begin_arg = cmd->convert_arg * cmd->scan_end_arg;
491 /* step 4: fix up and arguments */
497 static int timer_cmd(comedi_device *dev,comedi_subdevice *s)
500 comedi_cmd *cmd = &s->async->cmd;
502 /* hack attack: drivers are not supposed to do this: */
505 // make sure tasks have finished cleanup of last comedi_cmd
506 if(devpriv->rt_task_active || devpriv->scan_task_active)
509 ret = comedi_lock(devpriv->device,devpriv->subd);
512 comedi_error(dev, "failed to obtain lock");
515 switch(cmd->scan_begin_src){
517 devpriv->scan_period = nano2count(cmd->scan_begin_arg);
520 devpriv->scan_period = nano2count(cmd->convert_arg * cmd->scan_end_arg);
523 comedi_error(dev, "bug setting scan period!");
527 switch(cmd->convert_src){
529 devpriv->convert_period = nano2count(cmd->convert_arg);
532 devpriv->convert_period = 1;
535 comedi_error(dev, "bug setting conversion period!");
540 if(cmd->start_src == TRIG_NOW)
541 return timer_start_cmd(dev, s);
543 s->async->inttrig = timer_inttrig;
548 static int timer_inttrig(comedi_device *dev, comedi_subdevice *s, unsigned int trig_num)
553 s->async->inttrig = NULL;
555 return timer_start_cmd(dev, s);
558 static int timer_start_cmd(comedi_device *dev, comedi_subdevice *s)
560 comedi_async *async = s->async;
561 comedi_cmd *cmd = &async->cmd;
562 RTIME now, delay, period;
566 s->async->events = 0;
569 if(cmd->start_src == TRIG_NOW)
570 delay = nano2count(cmd->start_arg);
575 /* Using 'period' this way gets around some weird bug in gcc-2.95.2
576 * that generates the compile error 'internal error--unrecognizable insn'
577 * when rt_task_make_period() is called (observed with rtlinux-3.1, linux-2.2.19).
579 period = devpriv->scan_period;
580 ret = rt_task_make_periodic(devpriv->rt_task, now
584 comedi_error(dev, "error starting rt_task");
590 static int timer_attach(comedi_device *dev,comedi_devconfig *it)
593 comedi_subdevice *s, *emul_s;
594 comedi_device *emul_dev;
595 /* These should probably be devconfig options[] */
596 const int timer_priority = 4;
597 const int scan_priority = timer_priority + 1;
600 printk("comedi%d: timer: ",dev->minor);
602 dev->board_name="timer";
605 if((ret=alloc_subdevices(dev))<0)
607 if((ret=alloc_private(dev,sizeof(timer_private)))<0)
610 sprintf(path,"/dev/comedi%d",it->options[0]);
611 devpriv->device = comedi_open(path);
612 devpriv->subd=it->options[1];
614 printk("emulating commands for minor %i, subdevice %d\n", it->options[0], devpriv->subd);
616 emul_dev = devpriv->device;
617 emul_s = emul_dev->subdevices+devpriv->subd;
619 // input or output subdevice
621 s->type=emul_s->type;
622 s->subdev_flags = emul_s->subdev_flags;
623 s->n_chan=emul_s->n_chan;
624 s->len_chanlist=1024;
626 s->do_cmdtest=timer_cmdtest;
627 s->cancel=timer_cancel;
628 s->maxdata=emul_s->maxdata;
629 s->range_table=emul_s->range_table;
630 s->range_table_list=emul_s->range_table_list;
631 switch(emul_s->type){
633 s->insn_read=timer_insn;
634 dev->read_subdev = s;
635 devpriv->io_function = timer_data_read;
638 s->insn_write=timer_insn;
639 s->insn_read=timer_insn;
640 dev->write_subdev = s;
641 devpriv->io_function = timer_data_write;
643 case COMEDI_SUBD_DIO:
644 s->insn_write=timer_insn;
645 s->insn_read=timer_insn;
646 s->insn_bits=timer_insn;
647 dev->read_subdev = s;
648 devpriv->io_function = timer_dio_read;
651 comedi_error(dev, "failed to determine subdevice type!");
655 rt_set_oneshot_mode();
657 devpriv->timer_running = 1;
659 devpriv->rt_task = kmalloc(sizeof(RT_TASK),GFP_KERNEL);
660 memset(devpriv->rt_task,0,sizeof(RT_TASK));
662 // initialize real-time tasks
663 ret = rt_task_init(devpriv->rt_task, timer_task_func,(int)dev, 3000,
664 timer_priority, 0, 0);
666 comedi_error(dev, "error initalizing rt_task");
667 kfree(devpriv->rt_task);
668 devpriv->rt_task = 0;
672 devpriv->scan_task = kmalloc(sizeof(RT_TASK),GFP_KERNEL);
673 memset(devpriv->scan_task,0,sizeof(RT_TASK));
675 ret = rt_task_init(devpriv->scan_task, scan_task_func,
676 (int)dev, 3000, scan_priority, 0, 0);
678 comedi_error(dev, "error initalizing scan_task");
679 kfree(devpriv->scan_task);
680 devpriv->scan_task = 0;
687 // free allocated resources
688 static int timer_detach(comedi_device *dev)
690 printk("comedi%d: timer: remove\n",dev->minor);
693 if(devpriv->rt_task){
694 rt_task_delete(devpriv->rt_task);
695 kfree(devpriv->rt_task);
697 if(devpriv->scan_task){
698 rt_task_delete(devpriv->scan_task);
699 kfree(devpriv->scan_task);
701 if( devpriv->timer_running )