]> bbs.cooldavid.org Git - net-next-2.6.git/blame - drivers/staging/comedi/drivers/comedi_rt_timer.c
Staging: comedi: Undo stupid commit made 3 months ago
[net-next-2.6.git] / drivers / staging / comedi / drivers / comedi_rt_timer.c
CommitLineData
d96cba07
DS
1/*
2 comedi/drivers/comedi_rt_timer.c
3 virtual driver for using RTL timing sources
4
5 Authors: David A. Schleef, Frank M. Hess
6
7 COMEDI - Linux Control and Measurement Device Interface
8 Copyright (C) 1999,2001 David A. Schleef <ds@schleef.org>
9
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.
14
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.
19
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.
23
24**************************************************************************
25*/
26/*
27Driver: comedi_rt_timer
28Description: Command emulator using real-time tasks
29Author: ds, fmhess
30Devices:
31Status: works
32
33This driver requires RTAI or RTLinux to work correctly. It doesn't
34actually drive hardware directly, but calls other drivers and uses
35a real-time task to emulate commands for drivers and devices that
36are incapable of native commands. Thus, you can get accurately
37timed I/O on any device.
38
39Since the timing is all done in software, sampling jitter is much
40higher than with a device that has an on-board timer, and maximum
41sample rate is much lower.
42
43Configuration options:
44 [0] - minor number of device you wish to emulate commands for
45 [1] - subdevice number you wish to emulate commands for
46*/
47/*
48TODO:
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?
52*/
53
54#include "../comedidev.h"
55#include "../comedilib.h"
56
57#include "comedi_fc.h"
58
59#ifdef CONFIG_COMEDI_RTL_V1
60#include <rtl_sched.h>
61#include <asm/rt_irq.h>
62#endif
63#ifdef CONFIG_COMEDI_RTL
64#include <rtl.h>
65#include <rtl_sched.h>
66#include <rtl_compat.h>
67#include <asm/div64.h>
68
69#ifndef RTLINUX_VERSION_CODE
70#define RTLINUX_VERSION_CODE 0
71#endif
72#ifndef RTLINUX_VERSION
73#define RTLINUX_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
74#endif
75
2696fb57 76/* begin hack to workaround broken HRT_TO_8254() function on rtlinux */
d96cba07 77#if RTLINUX_VERSION_CODE <= RTLINUX_VERSION(3,0,100)
2696fb57 78/* this function sole purpose is to divide a long long by 838 */
d96cba07
DS
79static inline RTIME nano2count(long long ns)
80{
81 do_div(ns, 838);
82 return ns;
83}
84
85#ifdef rt_get_time()
86#undef rt_get_time()
87#endif
88#define rt_get_time() nano2count(gethrtime())
89
90#else
91
92#define nano2count(x) HRT_TO_8254(x)
93#endif
2696fb57 94/* end hack */
d96cba07 95
2696fb57 96/* rtl-rtai compatibility */
d96cba07
DS
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()
106
4bde29ed
IA
107#define comedi_rt_task_context_t int
108
d96cba07
DS
109#endif
110#ifdef CONFIG_COMEDI_RTAI
111#include <rtai.h>
112#include <rtai_sched.h>
5b32f439
IA
113#include <rtai_version.h>
114
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
123#define cv 0
124#define test1 0
125#define test2 0
126#define test3 0
127#endif
d96cba07
DS
128
129#if RTAI_VERSION_CODE < RTAI_MANGLE_VERSION(3,3,0)
130#define comedi_rt_task_context_t int
131#else
132#define comedi_rt_task_context_t long
133#endif
134
5b32f439
IA
135/* Finished checking RTAI_VERSION_CODE. */
136#undef cv
137#undef test1
138#undef test2
139#undef test3
140
d96cba07
DS
141#endif
142
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 */
147
0707bb04 148static int timer_attach(struct comedi_device * dev, struct comedi_devconfig * it);
71b5f4f1 149static int timer_detach(struct comedi_device * dev);
34c43922 150static int timer_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
d96cba07 151 unsigned int trig_num);
34c43922 152static int timer_start_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
d96cba07 153
139dfbdf 154static struct comedi_driver driver_timer = {
d96cba07
DS
155 module:THIS_MODULE,
156 driver_name:"comedi_rt_timer",
157 attach:timer_attach,
158 detach:timer_detach,
2696fb57 159/* open: timer_open, */
d96cba07
DS
160};
161
162COMEDI_INITCLEANUP(driver_timer);
163
ecce6332 164struct timer_private {
2696fb57
BP
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 */
d96cba07
DS
169 /* io_function can point to either an input or output function
170 * depending on what kind of subdevice we are emulating for */
ea6d0d4c 171 int (*io_function) (struct comedi_device * dev, struct comedi_cmd * cmd,
d96cba07 172 unsigned int index);
2696fb57
BP
173/*
174* RTIME has units of 1 = 838 nanoseconds time at which first scan
175* started, used to check scan timing
176*/
d96cba07 177 RTIME start;
2696fb57 178 /* time between scans */
d96cba07 179 RTIME scan_period;
2696fb57 180 /* time between conversions in a scan */
d96cba07 181 RTIME convert_period;
2696fb57
BP
182 /* flags */
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 */
d96cba07 186 unsigned timer_running:1;
ecce6332
BP
187};
188#define devpriv ((struct timer_private *)dev->private)
d96cba07 189
34c43922 190static int timer_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
d96cba07
DS
191{
192 devpriv->stop = 1;
193
194 return 0;
195}
196
2696fb57 197/* checks for scan timing error */
71b5f4f1 198inline static int check_scan_timing(struct comedi_device * dev,
d96cba07
DS
199 unsigned long long scan)
200{
201 RTIME now, timing_error;
202
203 now = rt_get_time();
204 timing_error = now - (devpriv->start + scan * devpriv->scan_period);
205 if (timing_error > devpriv->scan_period) {
206 comedi_error(dev, "timing error");
207 rt_printk("scan started %i ns late\n", timing_error * 838);
208 return -1;
209 }
210
211 return 0;
212}
213
2696fb57 214/* checks for conversion timing error */
71b5f4f1 215inline static int check_conversion_timing(struct comedi_device * dev,
d96cba07
DS
216 RTIME scan_start, unsigned int conversion)
217{
218 RTIME now, timing_error;
219
220 now = rt_get_time();
221 timing_error =
222 now - (scan_start + conversion * devpriv->convert_period);
223 if (timing_error > devpriv->convert_period) {
224 comedi_error(dev, "timing error");
225 rt_printk("conversion started %i ns late\n",
226 timing_error * 838);
227 return -1;
228 }
229
230 return 0;
231}
232
2696fb57 233/* devpriv->io_function for an input subdevice */
ea6d0d4c 234static int timer_data_read(struct comedi_device * dev, struct comedi_cmd * cmd,
d96cba07
DS
235 unsigned int index)
236{
34c43922 237 struct comedi_subdevice *s = dev->read_subdev;
d96cba07 238 int ret;
790c5541 239 unsigned int data;
d96cba07
DS
240
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);
245 if (ret < 0) {
246 comedi_error(dev, "read error");
247 return -EIO;
248 }
249 if (s->flags & SDF_LSAMPL) {
250 cfc_write_long_to_buffer(s, data);
251 } else {
252 comedi_buf_put(s->async, data);
253 }
254
255 return 0;
256}
257
2696fb57 258/* devpriv->io_function for an output subdevice */
ea6d0d4c 259static int timer_data_write(struct comedi_device * dev, struct comedi_cmd * cmd,
d96cba07
DS
260 unsigned int index)
261{
34c43922 262 struct comedi_subdevice *s = dev->write_subdev;
d96cba07 263 unsigned int num_bytes;
790c5541
BP
264 short data;
265 unsigned int long_data;
d96cba07
DS
266 int ret;
267
268 if (s->flags & SDF_LSAMPL) {
269 num_bytes =
270 cfc_read_array_from_buffer(s, &long_data,
271 sizeof(long_data));
272 } else {
273 num_bytes = cfc_read_array_from_buffer(s, &data, sizeof(data));
274 long_data = data;
275 }
276
277 if (num_bytes == 0) {
278 comedi_error(dev, "buffer underrun");
279 return -EAGAIN;
280 }
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);
285 if (ret < 0) {
286 comedi_error(dev, "write error");
287 return -EIO;
288 }
289
290 return 0;
291}
292
2696fb57 293/* devpriv->io_function for DIO subdevices */
ea6d0d4c 294static int timer_dio_read(struct comedi_device * dev, struct comedi_cmd * cmd,
d96cba07
DS
295 unsigned int index)
296{
34c43922 297 struct comedi_subdevice *s = dev->read_subdev;
d96cba07 298 int ret;
790c5541 299 unsigned int data;
d96cba07
DS
300
301 ret = comedi_dio_bitfield(devpriv->device, devpriv->subd, 0, &data);
302 if (ret < 0) {
303 comedi_error(dev, "read error");
304 return -EIO;
305 }
306
307 if (s->flags & SDF_LSAMPL)
308 cfc_write_long_to_buffer(s, data);
309 else
310 cfc_write_to_buffer(s, data);
311
312 return 0;
313}
314
2696fb57 315/* performs scans */
d96cba07
DS
316static void scan_task_func(comedi_rt_task_context_t d)
317{
71b5f4f1 318 struct comedi_device *dev = (struct comedi_device *) d;
34c43922 319 struct comedi_subdevice *s = dev->subdevices + 0;
d163679c 320 struct comedi_async *async = s->async;
ea6d0d4c 321 struct comedi_cmd *cmd = &async->cmd;
d96cba07
DS
322 int i, ret;
323 unsigned long long n;
324 RTIME scan_start;
325
2696fb57 326 /* every struct comedi_cmd causes one execution of while loop */
d96cba07
DS
327 while (1) {
328 devpriv->scan_task_active = 1;
2696fb57 329 /* each for loop completes one scan */
d96cba07
DS
330 for (n = 0; n < cmd->stop_arg || cmd->stop_src == TRIG_NONE;
331 n++) {
332 if (n) {
2696fb57 333 /* suspend task until next scan */
d96cba07
DS
334 ret = rt_task_suspend(devpriv->scan_task);
335 if (ret < 0) {
336 comedi_error(dev,
337 "error suspending scan task");
338 async->events |= COMEDI_CB_ERROR;
339 goto cleanup;
340 }
341 }
2696fb57 342 /* check if stop flag was set (by timer_cancel()) */
d96cba07
DS
343 if (devpriv->stop)
344 goto cleanup;
345 ret = check_scan_timing(dev, n);
346 if (ret < 0) {
347 async->events |= COMEDI_CB_ERROR;
348 goto cleanup;
349 }
350 scan_start = rt_get_time();
351 for (i = 0; i < cmd->scan_end_arg; i++) {
2696fb57 352 /* conversion timing */
d96cba07
DS
353 if (cmd->convert_src == TRIG_TIMER && i) {
354 rt_task_wait_period();
355 ret = check_conversion_timing(dev,
356 scan_start, i);
357 if (ret < 0) {
358 async->events |=
359 COMEDI_CB_ERROR;
360 goto cleanup;
361 }
362 }
363 ret = devpriv->io_function(dev, cmd, i);
364 if (ret < 0) {
365 async->events |= COMEDI_CB_ERROR;
366 goto cleanup;
367 }
368 }
369 s->async->events |= COMEDI_CB_BLOCK;
370 comedi_event(dev, s);
371 s->async->events = 0;
372 }
373
374 cleanup:
375
376 comedi_unlock(devpriv->device, devpriv->subd);
377 async->events |= COMEDI_CB_EOA;
378 comedi_event(dev, s);
379 async->events = 0;
380 devpriv->scan_task_active = 0;
2696fb57 381 /* suspend task until next struct comedi_cmd */
d96cba07
DS
382 rt_task_suspend(devpriv->scan_task);
383 }
384}
385
386static void timer_task_func(comedi_rt_task_context_t d)
387{
71b5f4f1 388 struct comedi_device *dev = (struct comedi_device *) d;
34c43922 389 struct comedi_subdevice *s = dev->subdevices + 0;
ea6d0d4c 390 struct comedi_cmd *cmd = &s->async->cmd;
d96cba07
DS
391 int ret;
392 unsigned long long n;
393
2696fb57 394 /* every struct comedi_cmd causes one execution of while loop */
d96cba07
DS
395 while (1) {
396 devpriv->rt_task_active = 1;
397 devpriv->scan_task_active = 1;
398 devpriv->start = rt_get_time();
399
400 for (n = 0; n < cmd->stop_arg || cmd->stop_src == TRIG_NONE;
401 n++) {
2696fb57 402 /* scan timing */
d96cba07
DS
403 if (n)
404 rt_task_wait_period();
405 if (devpriv->scan_task_active == 0) {
406 goto cleanup;
407 }
408 ret = rt_task_make_periodic(devpriv->scan_task,
409 devpriv->start + devpriv->scan_period * n,
410 devpriv->convert_period);
411 if (ret < 0) {
412 comedi_error(dev, "bug!");
413 }
414 }
415
416 cleanup:
417
418 devpriv->rt_task_active = 0;
2696fb57 419 /* suspend until next struct comedi_cmd */
d96cba07
DS
420 rt_task_suspend(devpriv->rt_task);
421 }
422}
423
34c43922 424static int timer_insn(struct comedi_device * dev, struct comedi_subdevice * s,
90035c08 425 struct comedi_insn * insn, unsigned int * data)
d96cba07 426{
90035c08 427 struct comedi_insn xinsn = *insn;
d96cba07
DS
428
429 xinsn.data = data;
430 xinsn.subdev = devpriv->subd;
431
432 return comedi_do_insn(devpriv->device, &xinsn);
433}
434
ea6d0d4c 435static int cmdtest_helper(struct comedi_cmd * cmd,
d96cba07
DS
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)
440{
441 int err = 0;
442 int tmp;
443
444 tmp = cmd->start_src;
445 cmd->start_src &= start_src;
446 if (!cmd->start_src || tmp != cmd->start_src)
447 err++;
448
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)
452 err++;
453
454 tmp = cmd->convert_src;
455 cmd->convert_src &= convert_src;
456 if (!cmd->convert_src || tmp != cmd->convert_src)
457 err++;
458
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)
462 err++;
463
464 tmp = cmd->stop_src;
465 cmd->stop_src &= stop_src;
466 if (!cmd->stop_src || tmp != cmd->stop_src)
467 err++;
468
469 return err;
470}
471
34c43922 472static int timer_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
ea6d0d4c 473 struct comedi_cmd * cmd)
d96cba07
DS
474{
475 int err = 0;
476 unsigned int start_src = 0;
477
478 if (s->type == COMEDI_SUBD_AO)
479 start_src = TRIG_INT;
480 else
481 start_src = TRIG_NOW;
482
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 */
488 if (err)
489 return 1;
490
491 /* step 2: make sure trigger sources are unique and mutually
492 * compatible */
493
494 if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_INT)
495 err++;
496 if (cmd->scan_begin_src != TRIG_TIMER &&
497 cmd->scan_begin_src != TRIG_FOLLOW)
498 err++;
499 if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_NOW)
500 err++;
501 if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
502 err++;
503 if (cmd->scan_begin_src == TRIG_FOLLOW
504 && cmd->convert_src != TRIG_TIMER)
505 err++;
506 if (cmd->convert_src == TRIG_NOW && cmd->scan_begin_src != TRIG_TIMER)
507 err++;
508
509 if (err)
510 return 2;
511
512 /* step 3: make sure arguments are trivially compatible */
2696fb57 513 /* limit frequency, this is fairly arbitrary */
d96cba07
DS
514 if (cmd->scan_begin_src == TRIG_TIMER) {
515 if (cmd->scan_begin_arg < SPEED_LIMIT) {
516 cmd->scan_begin_arg = SPEED_LIMIT;
517 err++;
518 }
519 }
520 if (cmd->convert_src == TRIG_TIMER) {
521 if (cmd->convert_arg < SPEED_LIMIT) {
522 cmd->convert_arg = SPEED_LIMIT;
523 err++;
524 }
525 }
2696fb57 526 /* make sure conversion and scan frequencies are compatible */
d96cba07
DS
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;
531 err++;
532 }
533 }
534 if (err)
535 return 3;
536
537 /* step 4: fix up and arguments */
538 if (err)
539 return 4;
540
541 return 0;
542}
543
34c43922 544static int timer_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
d96cba07
DS
545{
546 int ret;
ea6d0d4c 547 struct comedi_cmd *cmd = &s->async->cmd;
d96cba07
DS
548
549 /* hack attack: drivers are not supposed to do this: */
550 dev->rt = 1;
551
2696fb57 552 /* make sure tasks have finished cleanup of last struct comedi_cmd */
d96cba07
DS
553 if (devpriv->rt_task_active || devpriv->scan_task_active)
554 return -EBUSY;
555
556 ret = comedi_lock(devpriv->device, devpriv->subd);
557 if (ret < 0) {
558 comedi_error(dev, "failed to obtain lock");
559 return ret;
560 }
561 switch (cmd->scan_begin_src) {
562 case TRIG_TIMER:
563 devpriv->scan_period = nano2count(cmd->scan_begin_arg);
564 break;
565 case TRIG_FOLLOW:
566 devpriv->scan_period =
567 nano2count(cmd->convert_arg * cmd->scan_end_arg);
568 break;
569 default:
570 comedi_error(dev, "bug setting scan period!");
571 return -1;
572 break;
573 }
574 switch (cmd->convert_src) {
575 case TRIG_TIMER:
576 devpriv->convert_period = nano2count(cmd->convert_arg);
577 break;
578 case TRIG_NOW:
579 devpriv->convert_period = 1;
580 break;
581 default:
582 comedi_error(dev, "bug setting conversion period!");
583 return -1;
584 break;
585 }
586
587 if (cmd->start_src == TRIG_NOW)
588 return timer_start_cmd(dev, s);
589
590 s->async->inttrig = timer_inttrig;
591
592 return 0;
593}
594
34c43922 595static int timer_inttrig(struct comedi_device * dev, struct comedi_subdevice * s,
d96cba07
DS
596 unsigned int trig_num)
597{
598 if (trig_num != 0)
599 return -EINVAL;
600
601 s->async->inttrig = NULL;
602
603 return timer_start_cmd(dev, s);
604}
605
34c43922 606static int timer_start_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
d96cba07 607{
d163679c 608 struct comedi_async *async = s->async;
ea6d0d4c 609 struct comedi_cmd *cmd = &async->cmd;
d96cba07
DS
610 RTIME now, delay, period;
611 int ret;
612
613 devpriv->stop = 0;
614 s->async->events = 0;
615
616 if (cmd->start_src == TRIG_NOW)
617 delay = nano2count(cmd->start_arg);
618 else
619 delay = 0;
620
621 now = rt_get_time();
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).
625 * - fmhess */
626 period = devpriv->scan_period;
627 ret = rt_task_make_periodic(devpriv->rt_task, now + delay, period);
628 if (ret < 0) {
629 comedi_error(dev, "error starting rt_task");
630 return ret;
631 }
632 return 0;
633}
634
0707bb04 635static int timer_attach(struct comedi_device * dev, struct comedi_devconfig * it)
d96cba07
DS
636{
637 int ret;
34c43922 638 struct comedi_subdevice *s, *emul_s;
71b5f4f1 639 struct comedi_device *emul_dev;
d96cba07
DS
640 /* These should probably be devconfig options[] */
641 const int timer_priority = 4;
642 const int scan_priority = timer_priority + 1;
643 char path[20];
644
645 printk("comedi%d: timer: ", dev->minor);
646
647 dev->board_name = "timer";
648
649 if ((ret = alloc_subdevices(dev, 1)) < 0)
650 return ret;
ecce6332 651 if ((ret = alloc_private(dev, sizeof(struct timer_private))) < 0)
d96cba07
DS
652 return ret;
653
654 sprintf(path, "/dev/comedi%d", it->options[0]);
655 devpriv->device = comedi_open(path);
656 devpriv->subd = it->options[1];
657
658 printk("emulating commands for minor %i, subdevice %d\n",
659 it->options[0], devpriv->subd);
660
661 emul_dev = devpriv->device;
662 emul_s = emul_dev->subdevices + devpriv->subd;
663
2696fb57 664 /* input or output subdevice */
d96cba07
DS
665 s = dev->subdevices + 0;
666 s->type = emul_s->type;
667 s->subdev_flags = emul_s->subdev_flags; /* SDF_GROUND (to fool check_driver) */
668 s->n_chan = emul_s->n_chan;
669 s->len_chanlist = 1024;
670 s->do_cmd = timer_cmd;
671 s->do_cmdtest = timer_cmdtest;
672 s->cancel = timer_cancel;
673 s->maxdata = emul_s->maxdata;
674 s->range_table = emul_s->range_table;
675 s->range_table_list = emul_s->range_table_list;
676 switch (emul_s->type) {
677 case COMEDI_SUBD_AI:
678 s->insn_read = timer_insn;
679 dev->read_subdev = s;
680 s->subdev_flags |= SDF_CMD_READ;
681 devpriv->io_function = timer_data_read;
682 break;
683 case COMEDI_SUBD_AO:
684 s->insn_write = timer_insn;
685 s->insn_read = timer_insn;
686 dev->write_subdev = s;
687 s->subdev_flags |= SDF_CMD_WRITE;
688 devpriv->io_function = timer_data_write;
689 break;
690 case COMEDI_SUBD_DIO:
691 s->insn_write = timer_insn;
692 s->insn_read = timer_insn;
693 s->insn_bits = timer_insn;
694 dev->read_subdev = s;
695 s->subdev_flags |= SDF_CMD_READ;
696 devpriv->io_function = timer_dio_read;
697 break;
698 default:
699 comedi_error(dev, "failed to determine subdevice type!");
700 return -EINVAL;
701 }
702
703 rt_set_oneshot_mode();
704 start_rt_timer(1);
705 devpriv->timer_running = 1;
706
707 devpriv->rt_task = kzalloc(sizeof(RT_TASK), GFP_KERNEL);
708
2696fb57 709 /* initialize real-time tasks */
d96cba07
DS
710 ret = rt_task_init(devpriv->rt_task, timer_task_func,
711 (comedi_rt_task_context_t) dev, 3000, timer_priority, 0, 0);
712 if (ret < 0) {
713 comedi_error(dev, "error initalizing rt_task");
714 kfree(devpriv->rt_task);
715 devpriv->rt_task = 0;
716 return ret;
717 }
718
719 devpriv->scan_task = kzalloc(sizeof(RT_TASK), GFP_KERNEL);
720
721 ret = rt_task_init(devpriv->scan_task, scan_task_func,
722 (comedi_rt_task_context_t) dev, 3000, scan_priority, 0, 0);
723 if (ret < 0) {
724 comedi_error(dev, "error initalizing scan_task");
725 kfree(devpriv->scan_task);
726 devpriv->scan_task = 0;
727 return ret;
728 }
729
730 return 1;
731}
732
2696fb57 733/* free allocated resources */
71b5f4f1 734static int timer_detach(struct comedi_device * dev)
d96cba07
DS
735{
736 printk("comedi%d: timer: remove\n", dev->minor);
737
738 if (devpriv) {
739 if (devpriv->rt_task) {
740 rt_task_delete(devpriv->rt_task);
741 kfree(devpriv->rt_task);
742 }
743 if (devpriv->scan_task) {
744 rt_task_delete(devpriv->scan_task);
745 kfree(devpriv->scan_task);
746 }
747 if (devpriv->timer_running)
748 stop_rt_timer();
749 if (devpriv->device)
750 comedi_close(devpriv->device);
751 }
752 return 0;
753}