📄 comedi_rt_timer.c
字号:
/* module/comedi_rt_timer.c virtual driver for using RTL timing sources Authors: David A. Schleef, Frank M. Hess COMEDI - Linux Control and Measurement Device Interface Copyright (C) 1999,2001 David A. Schleef <ds@schleef.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.***************************************************************************//*Driver: comedi_rt_timer.oDescription: Command emulator using real-time tasksAuthor: ds, fmhessDevices:Status: worksThis driver requires RTAI or RTLinux to work correctly. It doesn'tactually drive hardware directly, but calls other drivers and usesa real-time task to emulate commands for drivers and devices thatare incapable of native commands. Thus, you can get accuratelytimed I/O on any device.Since the timing is all done in software, sampling jitter is muchhigher than with a device that has an on-board timer, and maximumsample rate is much lower.Configuration options: [0] - minor number of device you wish to emulate commands for [1] - subdevice number you wish to emulate commands for*//*TODO: Support for digital io commands could be added, except I can't see why anyone would want to use them What happens if device we are emulating for is de-configured?*/#include <linux/comedidev.h>#include <linux/comedilib.h>#include "comedi_fc.h"#ifdef CONFIG_COMEDI_RTL_V1#include <rtl_sched.h>#include <asm/rt_irq.h>#endif#ifdef CONFIG_COMEDI_RTL#include <rtl.h>#include <rtl_sched.h>#include <rtl_compat.h>#include <asm/div64.h>#ifndef RTLINUX_VERSION_CODE#define RTLINUX_VERSION_CODE 0#endif#ifndef RTLINUX_VERSION#define RTLINUX_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))#endif// begin hack to workaround broken HRT_TO_8254() function on rtlinux#if RTLINUX_VERSION_CODE <= RTLINUX_VERSION(3,0,100)// this function sole purpose is to divide a long long by 838static inline RTIME nano2count(long long ns){ do_div(ns, 838); return ns;}#ifdef rt_get_time()#undef rt_get_time()#endif#define rt_get_time() nano2count(gethrtime())#else#define nano2count(x) HRT_TO_8254(x)#endif// end hack// rtl-rtai compatibility#define rt_task_wait_period() rt_task_wait()#define rt_pend_linux_srq(irq) rtl_global_pend_irq(irq)#define rt_free_srq(irq) rtl_free_soft_irq(irq)#define rt_request_srq(x,y,z) rtl_get_soft_irq(y,"timer")#define rt_task_init(a,b,c,d,e,f,g) rt_task_init(a,b,c,d,(e)+1)#define rt_task_resume(x) rt_task_wakeup(x)#define rt_set_oneshot_mode()#define start_rt_timer(x)#define stop_rt_timer()#endif#ifdef CONFIG_COMEDI_RTAI#include <rtai.h>#include <rtai_sched.h>#endif/* This defines the fastest speed we will emulate. Note that * without a watchdog (like in RTAI), we could easily overrun our * task period because analog input tends to be slow. */#define SPEED_LIMIT 100000 /* in nanoseconds */static int timer_attach(comedi_device *dev,comedi_devconfig *it);static int timer_detach(comedi_device *dev);static int timer_inttrig(comedi_device *dev, comedi_subdevice *s, unsigned int trig_num);static int timer_start_cmd(comedi_device *dev, comedi_subdevice *s);static comedi_driver driver_timer={ module: THIS_MODULE, driver_name: "comedi_rt_timer", attach: timer_attach, detach: timer_detach,// open: timer_open,};COMEDI_INITCLEANUP(driver_timer);typedef struct{ comedi_t *device; // device we are emulating commands for int subd; // subdevice we are emulating commands for RT_TASK *rt_task; // rt task that starts scans RT_TASK *scan_task; // rt task that controls conversion timing in a scan /* io_function can point to either an input or output function * depending on what kind of subdevice we are emulating for */ int (*io_function)(comedi_device *dev, comedi_cmd *cmd, unsigned int index); // RTIME has units of 1 = 838 nanoseconds // time at which first scan started, used to check scan timing RTIME start; // time between scans RTIME scan_period; // time between conversions in a scan RTIME convert_period; // flags volatile int stop; // indicates we should stop volatile int rt_task_active; // indicates rt_task is servicing a comedi_cmd volatile int scan_task_active; // indicates scan_task is servicing a comedi_cmd unsigned timer_running : 1;}timer_private;#define devpriv ((timer_private *)dev->private)static int timer_cancel(comedi_device *dev,comedi_subdevice *s){ devpriv->stop = 1; return 0;}// checks for scan timing errorinline static int check_scan_timing(comedi_device *dev, unsigned long long scan){ RTIME now, timing_error; now = rt_get_time(); timing_error = now - (devpriv->start + scan * devpriv->scan_period); if(timing_error > devpriv->scan_period){ comedi_error(dev, "timing error"); rt_printk("scan started %i ns late\n", timing_error * 838); return -1; } return 0;}// checks for conversion timing errorinline static int check_conversion_timing(comedi_device *dev, RTIME scan_start, unsigned int conversion){ RTIME now, timing_error; now = rt_get_time(); timing_error = now - (scan_start + conversion * devpriv->convert_period); if(timing_error > devpriv->convert_period){ comedi_error(dev, "timing error"); rt_printk("conversion started %i ns late\n", timing_error * 838); return -1; } return 0;}// devpriv->io_function for an input subdevicestatic int timer_data_read(comedi_device *dev, comedi_cmd *cmd, unsigned int index){ comedi_subdevice *s = dev->read_subdev; int ret; lsampl_t data; ret = comedi_data_read(devpriv->device, devpriv->subd, CR_CHAN(cmd->chanlist[index]), CR_RANGE(cmd->chanlist[index]), CR_AREF(cmd->chanlist[index]), &data); if(ret<0){ comedi_error(dev, "read error"); return -EIO; } if( s->flags & SDF_LSAMPL ){ cfc_write_long_to_buffer( s, data ); }else{ comedi_buf_put( s->async, data ); } return 0;}// devpriv->io_function for an output subdevicestatic int timer_data_write(comedi_device *dev, comedi_cmd *cmd, unsigned int index){ comedi_subdevice *s = dev->write_subdev; unsigned int num_bytes; sampl_t data; lsampl_t long_data; int ret; if( s->flags & SDF_LSAMPL ) { num_bytes = cfc_read_array_from_buffer( s, &long_data, sizeof( long_data ) ); }else{ num_bytes = cfc_read_array_from_buffer( s, &data, sizeof( data ) ); long_data = data; } if(num_bytes == 0) { comedi_error(dev, "buffer underrun"); return -EAGAIN; } ret = comedi_data_write(devpriv->device, devpriv->subd, CR_CHAN(cmd->chanlist[index]), CR_RANGE(cmd->chanlist[index]), CR_AREF(cmd->chanlist[index]), long_data); if(ret<0){ comedi_error(dev, "write error"); return -EIO; } return 0;}// devpriv->io_function for DIO subdevicesstatic int timer_dio_read(comedi_device *dev, comedi_cmd *cmd, unsigned int index){ comedi_subdevice *s = dev->read_subdev; int ret; lsampl_t data; ret = comedi_dio_bitfield(devpriv->device, devpriv->subd, 0, &data); if(ret<0){ comedi_error(dev, "read error"); return -EIO; } if( s->flags & SDF_LSAMPL ) cfc_write_long_to_buffer( s, data ); else cfc_write_to_buffer( s, data ); return 0;}// performs scansstatic void scan_task_func(int d){ comedi_device *dev=(comedi_device *)d; comedi_subdevice *s = dev->subdevices + 0; comedi_async *async = s->async; comedi_cmd *cmd = &async->cmd; int i, ret; unsigned long long n; RTIME scan_start; // every comedi_cmd causes one execution of while loop while(1){ devpriv->scan_task_active = 1; // each for loop completes one scan for(n = 0; n < cmd->stop_arg || cmd->stop_src == TRIG_NONE; n++){ if(n){ // suspend task until next scan ret = rt_task_suspend(devpriv->scan_task); if(ret < 0){ comedi_error(dev, "error suspending scan task"); async->events |= COMEDI_CB_ERROR; goto cleanup; } } // check if stop flag was set (by timer_cancel()) if(devpriv->stop) goto cleanup; ret = check_scan_timing(dev, n); if(ret < 0){ async->events |= COMEDI_CB_ERROR; goto cleanup; } scan_start = rt_get_time(); for(i = 0; i < cmd->scan_end_arg; i++){ // conversion timing if(cmd->convert_src == TRIG_TIMER && i){ rt_task_wait_period(); ret = check_conversion_timing(dev, scan_start, i); if(ret < 0){ async->events |= COMEDI_CB_ERROR; goto cleanup; } } ret = devpriv->io_function(dev, cmd, i); if(ret < 0){ async->events |= COMEDI_CB_ERROR; goto cleanup; } } s->async->events |= COMEDI_CB_BLOCK; comedi_event(dev,s,s->async->events); s->async->events = 0; }cleanup: comedi_unlock(devpriv->device,devpriv->subd); async->events |= COMEDI_CB_EOA; comedi_event(dev, s, async->events); async->events = 0; devpriv->scan_task_active = 0; // suspend task until next comedi_cmd rt_task_suspend(devpriv->scan_task); }}static void timer_task_func(int d){ comedi_device *dev=(comedi_device *)d; comedi_subdevice *s = dev->subdevices + 0; comedi_cmd *cmd=&s->async->cmd; int ret; unsigned long long n;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -