typedef struct{
- int device;
- int subd;
- RT_TASK rt_task;
- RT_TASK scan_task;
+ int 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;
- unsigned done : 1;
+ // flags
+ unsigned stop : 1; // indicates we should stop
+ unsigned rt_task_active : 1; // indicates rt_task is servicing a comedi_cmd
+ unsigned scan_task_active : 1; // indicates scan_task is servicing a comedi_cmd
}timer_private;
#define devpriv ((timer_private *)dev->private)
static int timer_cancel(comedi_device *dev,comedi_subdevice *s)
{
- devpriv->done = 1;
+ devpriv->stop = 1;
return 0;
}
return 0;
}
-// performs input scans
-static void input_scan_task_func(int d)
+// devpriv->io_function for an input subdevice
+static int timer_data_read(comedi_device *dev, comedi_cmd *cmd,
+ unsigned int index)
{
- comedi_device *dev=(comedi_device *)d;
- comedi_subdevice *s=dev->read_subdev;
- comedi_cmd *cmd=&s->async->cmd;
- int i, ret;
- unsigned long long n;
+ comedi_subdevice *s = dev->read_subdev;
+ int ret;
lsampl_t data;
- RTIME scan_start;
- // every comedi_cmd causes one execution of while loop
- while(1){
- s->async->events = 0;
+ 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;
+ }
+ buf_add(dev, s, data);
- // 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");
- comedi_error_done(dev,s);
- goto cleanup;
- }
- }
- // check if done flag was set (by timer_cancel())
- if(devpriv->done)
- goto cleanup;
- ret = check_scan_timing(dev, n);
- if(ret < 0){
- comedi_error_done(dev,s);
- 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){
- comedi_error_done(dev,s);
- goto cleanup;
- }
- }
- ret = comedi_data_read(devpriv->device,devpriv->subd,
- CR_CHAN(cmd->chanlist[i]),
- CR_RANGE(cmd->chanlist[i]),
- CR_AREF(cmd->chanlist[i]),
- &data);
- if(ret<0){
- comedi_error(dev, "read error");
- comedi_error_done(dev, s);
- goto cleanup;
- }
- buf_add(dev,s,data);
- }
- s->async->events |= COMEDI_CB_EOS | COMEDI_CB_BLOCK;
- comedi_event(dev,s,s->async->events);
- s->async->events = 0;
- }
- comedi_done(dev, s);
+ return 0;
+}
-cleanup:
+// devpriv->io_function for an output subdevice
+static int timer_data_write(comedi_device *dev, comedi_cmd *cmd,
+ unsigned int index)
+{
+ comedi_subdevice *s = dev->write_subdev;
+ int ret;
+ int data;
- devpriv->done = 1;
- comedi_unlock(devpriv->device,devpriv->subd);
- // suspend task until next comedi_cmd
- rt_task_suspend(&devpriv->scan_task);
+ data = buf_remove(dev, s);
+ if(data < 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]),
+ data);
+ if(ret<0){
+ comedi_error(dev, "write error");
+ return -EIO;
+ }
+
+ return 0;
}
-// performs output scans
-static void output_scan_task_func(int d)
+// performs scans
+static void scan_task_func(int d)
{
comedi_device *dev=(comedi_device *)d;
- comedi_subdevice *s=dev->write_subdev;
+ comedi_subdevice *s = dev->subdevices + 0;
comedi_cmd *cmd=&s->async->cmd;
int i, ret;
unsigned long long n;
- int data;
RTIME scan_start;
// every comedi_cmd causes one execution of while loop
while(1){
- s->async->events = 0;
-
+ devpriv->scan_task_active = 1;
// each for loop completes one scan
for(n = 0; n < cmd->stop_arg || cmd->stop_src == TRIG_NONE; n++){
- // suspend task until next scan
if(n){
+ // suspend task until next scan
ret = rt_task_suspend(&devpriv->scan_task);
if(ret < 0){
comedi_error(dev, "error suspending scan task");
goto cleanup;
}
}
- // check if done flag was set (by timer_cancel())
- if(devpriv->done)
+ // check if stop flag was set (by timer_cancel())
+ if(devpriv->stop)
goto cleanup;
ret = check_scan_timing(dev, n);
if(ret < 0){
goto cleanup;
}
}
- data = buf_remove(dev, s);
- if(data < 0) {
- comedi_error(dev, "buffer underrun");
- comedi_error_done(dev, s);
- goto cleanup;
- }
- ret = comedi_data_write(devpriv->device,devpriv->subd,
- CR_CHAN(cmd->chanlist[i]),
- CR_RANGE(cmd->chanlist[i]),
- CR_AREF(cmd->chanlist[i]),
- data);
- if(ret<0){
- comedi_error(dev, "write error");
+ ret = devpriv->io_function(dev, cmd, i);
+ if(ret < 0){
comedi_error_done(dev, s);
goto cleanup;
}
cleanup:
- devpriv->done = 1;
comedi_unlock(devpriv->device,devpriv->subd);
- // suspend until next comedi_cmd
+ devpriv->scan_task_active = 0;
+ // suspend task until next comedi_cmd
rt_task_suspend(&devpriv->scan_task);
}
}
// every comedi_cmd causes one execution of while loop
while(1){
+ devpriv->rt_task_active = 1;
+ devpriv->scan_task_active = 1;
devpriv->start = rt_get_time();
for(n = 0; 1; n++){
// scan timing
if(n)
rt_task_wait_period();
- if(devpriv->done){
+ if(devpriv->scan_task_active == 0){
goto cleanup;
}
ret = rt_task_make_periodic(&devpriv->scan_task,
cleanup:
+ devpriv->rt_task_active = 0;
// suspend until next comedi_cmd
rt_task_suspend(&devpriv->rt_task);
}
/* hack attack: drivers are not supposed to do this: */
dev->rt = 1;
+ // make sure tasks have finished cleanup of last comedi_cmd
+ if(devpriv->rt_task_active || devpriv->scan_task_active)
+ return -EBUSY;
+
ret = comedi_lock(devpriv->device,devpriv->subd);
if(ret<0)
{
start_rt_timer(1);
#endif
- devpriv->done = 0;
+ devpriv->stop = 0;
+ s->async->events = 0;
now=rt_get_time();
ret = rt_task_make_periodic(&devpriv->rt_task, now
dev->board_name="timer";
- dev->n_subdevices=2;
+ dev->n_subdevices=1;
if((ret=alloc_subdevices(dev))<0)
return ret;
if((ret=alloc_private(dev,sizeof(timer_private)))<0)
return -EINVAL;
}
- // input subdevice
+ // input or output subdevice
s=dev->subdevices+0;
if(emul_s->subdev_flags & SDF_READABLE)
{
s->range_table_list=emul_s->range_table_list;
s->insn_read=timer_insn;
dev->read_subdev = s;
- }else {
- s->type=COMEDI_SUBD_UNUSED;
- }
-
- // output subdevice
- s=dev->subdevices+1;
- if(emul_s->subdev_flags & SDF_WRITEABLE)
+ devpriv->io_function = timer_data_read;
+ }else if(emul_s->subdev_flags & SDF_WRITEABLE)
{
s->type=emul_s->type;
s->subdev_flags = SDF_WRITEABLE;
s->insn_write=timer_insn;
s->insn_read=timer_insn;
dev->write_subdev = s;
+ devpriv->io_function = timer_data_write;
}else {
- s->type=COMEDI_SUBD_UNUSED;
+ comedi_error(dev, "subdevice is not readable or writeable!");
+ return -EINVAL;
}
// initialize real-time tasks
return ret;
}
- if(dev->read_subdev){
- ret = rt_task_init(&devpriv->scan_task, input_scan_task_func,
- (int)dev, 3000, scan_priority, 0, 0);
- }else if(dev->write_subdev){
- ret = rt_task_init(&devpriv->scan_task, output_scan_task_func,
- (int)dev, 3000, scan_priority, 0, 0);
- }else{
- comedi_error(dev, "no read or write subdevice?");
- ret = -EINVAL;
- }
+ ret = rt_task_init(&devpriv->scan_task, scan_task_func,
+ (int)dev, 3000, scan_priority, 0, 0);
if(ret < 0){
comedi_error(dev, "error initalizing scan_task");
return ret;