[prev in list] [next in list] [prev in thread] [next in thread] 

List:       comedi
Subject:    Re: RTAI and Comedi: Problems with commands
From:       "Andreas Leuner" <al14 () inf ! tu-dresden ! de>
Date:       2006-11-07 15:59:18
Message-ID: 58008.145.254.71.82.1162915158.squirrel () mail ! inf ! tu-dresden ! de
[Download RAW message or body]

> Hi everybody,
>
> I am trying to use the comedi commands using RTAI (RTAI 3.3CV, latest
> comedi/comedilib). On the RTAI CVS I have found an (old?) example
testa.c > that I used as base.
> I have debugged into the driver (I use a simple PCI NI 6024-E) and I found
> out that the data is transferred correctly via DMA to the async
> prealloc_buf.

I have used both a PCI NI 6024-E and a DAQCard NI 6024-E successfully
(read on!) ...

> It seams as the only thing that misses is the transfer from the internal
> (kernel space) memory that holds the input values (written by DMA) to the
> user space value.
>
> One strange thing that I have detected was the following:
> The function rt_comedi_alloc_cmd() sets internally the values of cmd->data
> and cmd->chanlist and returns the values in the arguments **chanlist and
> **data.
> However, when testa returns from comedi_command_test() the value of
> cmd->data has changed - it points now somewhere in a 0xexxxxxxx address
> space.
> Could this be an issue?
Exactly...

The buffer address returned by comedi_command_test() seems to be a kernel
space address and not directly usable by an lxrt userspace program.

The LXRT support for comedi _commands_ has been broken for a few years
(since 2002?) now.

At that time the comedi drivers must have been changed to no longer access
the buffer made known to them via "data" in rt_comedi_alloc_cmd().
Instead they read from/write to a transfer buffer preallocated per
subdevice at module load time or even only at the time a device is
configured.

LXRT-Comedi has never been accomodated to that change.
>
> Is there somewhere a running example available that shows working comedi
> commands with RTAI?

I had to make that stuff work for myself last year -- a little rewrite of
rtai_comedi.h and kcomedi_module.c from addons/comedi subdirectory of rtai
sources.

They can be used as drop-in replacements for the original files from RTAI
versions from at least 3.1. However they miss support for one kcomedilib
call that has been added to magma CVS meanwhile.

I have made a new version of rt_comedi_alloc_cmd -- its interface is no
longer source compatible to the old one.
Please be aware that its method to make the buffer available to lxrt user
space is at least questionable -- but it works for me.

The userspace non-rt comedilib just makes the transfer buffer available as
a mmap()able device (/dev/comediX) -- which is far better but not
supported by _kcomedilib_.

My kernel-side implementation of rt_comedi_alloc_cmd _fakes_ the
allocation of a SHM segment for the transfer buffer and provides its lxrt
name for userspace. That might be *dangerous* if the module is removed
before rt_comedi_free_cmd has been called. Just look at a dmesg trace of
this.

I'll attach my modified files to this mail together with a modified
"testa.c" example. In it you'll find a use case for
rt_comedi_mark_wait_munge(..) -- which I added to LXRT-Comedi.

My solution is by no means elegant -- better would be to modify kcomedilib
to support userspace mmap()ing /dev/comediX just like comedilib does.
Look at comedilib/demo/mmap.c of the comedilib distribution for an example
of using that support.

Since these files are derivatives of RTAI sources I offer them under the
same licenses and the same warranty disclaimer :-)

Good luck

  Andreas Leuner

["kcomedi-module.c" (text/x-csrc)]

/*
 * Copyright (C) 2002 Thomas Leibner (leibner@t-online.de) (first complete writeup)
 *               2002 David Schleef (ds@schleef.org) (COMEDI master)
 *               2002 Lorenzo Dozio (dozio@aero.polimi.it) (made it all work)
 *               2002 Paolo Mantegazza (mantegazza@aero.polimi.it) (hints/support)
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/**
 * This file is the kernel module counterpart of the KComedi-LXRT support.
 * It will be compiled to a rtai_comedi.o kernel module.
 */

#include <linux/module.h>
#include <linux/version.h>
#include <asm/uaccess.h>

#include <rtai_sched.h>
#include <rtai_lxrt.h>
#include <rtai_shm.h>
#include <rtai_registry.h>

#include <rtai_comedi.h>

#define MODULE_NAME "rtai_comedi.o"
MODULE_DESCRIPTION("RTAI LXRT binding for COMEDI kcomedilib");
MODULE_AUTHOR("Thomas Leibner <tl@leibner-it.de>");
MODULE_LICENSE("GPL");

static int rtai_comedi_callback(unsigned int, SEM *) __attribute__ ((__unused__));
static int rtai_comedi_callback(unsigned int val, SEM *sem)
{
	sem->owndby = (void *)val;
	//rt_printk("CALLBACK MASK: %u\n", val);
	rt_sem_signal(sem);
	return 0;
}

int rt_comedi_register_callback(void *dev, unsigned int subdev, unsigned int mask, \
SEM *sem) {
	sem->type = (CNT_SEM & 3) - 2;
	return comedi_register_callback(dev, subdev, mask, (void *)rtai_comedi_callback, \
sem); }

unsigned int rt_comedi_wait(SEM *sem, int *semcnt)
{
	int count;
	count = rt_sem_wait(sem);
        if (semcnt) {
                *semcnt = count;
        }
	return (unsigned int)sem->owndby;
}

unsigned int rt_comedi_mark_wait_munge( void *dev, unsigned int subdev, int oldbytes, \
int *newbytes, SEM *sem, int *semcnt ) {
	int count;
	//newbytes _must not_ be NULL - for now still checked
	if (newbytes == 0)
	{
	    rt_printk("rt_comedi_mark_wait_munge: newbytes==0");
	    return 0;
	}
	if (sem == 0)
	{
	    rt_printk("rt_comedi_mark_wait_munge: sem==0");
	    return 0;
	}
	if (oldbytes != 0) {
		int type;
		type = comedi_get_subdevice_type(dev, subdev);
		switch (type){
			case COMEDI_SUBD_AI :
			case COMEDI_SUBD_DI : comedi_mark_buffer_read(dev,subdev,oldbytes); break;

/*			comedi_mark_buffer_written is not (yet) part of kcomedilib api
			case COMEDI_SUBD_AO :
			case COMEDI_SUBD_DO : comedi_mark_buffer_written(dev,subdev,oldbytes); break;
*/
			//TODO what about bidirectional devices? find out if DI or DO in progress
			case COMEDI_SUBD_DIO: break;
			default: return 0;
		}
	}
	count = rt_sem_wait(sem);

	*newbytes = comedi_get_buffer_contents(dev,subdev);

        if (semcnt) {
                *semcnt = count;
        }
	return (unsigned int)sem->owndby;
}

unsigned int rt_comedi_wait_if(SEM *sem, int *semcnt)
{
	int count;
	count = rt_sem_wait_if(sem);
        if (semcnt) {
                *semcnt = count;
        }
	return (unsigned int)sem->owndby;
}

unsigned int rt_comedi_wait_until(SEM *sem, RTIME until, int *semcnt)
{
	int count;
	count = rt_sem_wait_until(sem, until);
        if (semcnt) {
                *semcnt = count;
        }
	return (unsigned int)sem->owndby;
}

unsigned int rt_comedi_wait_timed(SEM *sem, RTIME delay, int *semcnt)
{
	int count;
	count = rt_sem_wait_timed(sem, delay);
        if (semcnt) {
                *semcnt = count;
        }
	return (unsigned int)sem->owndby;
}

char *rt_comedi_get_driver_name(void *dev, char *name)
{
	void *p;
	if ((p = comedi_get_driver_name((void *)dev)) != 0) {
	        strncpy(name, p, COMEDI_NAMELEN);
		return name;
	};
	return 0;
}

char *rt_comedi_get_board_name(void *dev, char *name)
{
        void *p;
	if ((p = comedi_get_board_name((void *)dev)) != 0) {
	        strncpy(name, p, COMEDI_NAMELEN);
	        return name;
	}
	return 0;
}

struct rt_cmd { comedi_cmd cmd; void *kcmd, *kchanlist, *kdata; unsigned long name; \
}; static unsigned long namebase = (unsigned long)&namebase;

static int rt_comedi_command(void *dev, struct rt_cmd *cmd)
{
	struct rt_cmd *kcmd;

	get_user(kcmd, &cmd->kcmd);
	kcmd->cmd.chanlist = kcmd->kchanlist;
	kcmd->cmd.data = kcmd->kdata;

	return comedi_command(dev, (void *)kcmd);
}

static int rt_comedi_command_test(void *dev, struct rt_cmd *cmd)
{
	struct rt_cmd *kcmd;

	get_user(kcmd, &cmd->kcmd);
	kcmd->cmd.chanlist = kcmd->kchanlist;
	kcmd->cmd.data = kcmd->kdata;
	return comedi_command_test(dev, (void *)kcmd);
}

/**
 * allocates a RTAI shared memory area for a comedi_cmd structure, a channel
 * list, and provides a RTAI <em>name</em> for the asynchronous DAQ transfer buffer.
 *
 * This function makes memory available for structures relevant for asynchronous
 * data acquisition. A shared memory area is allocated for the comedi_cmd
 * structure and the channel list array. Further the transfer buffer which is
 * maintained by comedi's DAQ driver is registered as a RTAI SHM segment to be
 * available under a RTAI <em>name</em>.
 *
 * @param dev (must be) a pointer to a comedi_t structure
 * @param subdev the number of the comedi subdevice to perform acquistion on
 * @param chanlist_len the length of the channel list
 * @param ofst contains pointers to implicit rt_cmd structure's members
 * @return name of a shm area for comedi_cmd structure.
 */
static unsigned long rt_comedi_alloc_cmd(void *dev, unsigned int subdev, unsigned int \
chanlist_len, unsigned long *ofst ) {
	struct rt_cmd *kcmd;
	unsigned long name, namebuf;
	int bufsz,result;
	name = namebase++;
	namebuf = namebase++;
        void *async_buf = NULL;

	if ( 0 != (result = comedi_map(dev, subdev, &async_buf)) )
	{
		rt_printk
		(
			"rt_comedi_alloc_cmd: ERROR getting subdev buffer pointer: comedi_map: %i.\n",
			result
		);
	}
	else
	{
		int size;
		//rt_printk("rt_comedi_alloc_cmd: Got subdev buffer pointer: %p\n", async_buf);

		if ( 0 < (bufsz = comedi_get_buffer_size(dev,subdev)) )
		{
			//third parameter specifies the type of the resource

                        //mimic a RTAI shm allocation (see rtai-core/ipc/shm/shm.c)
                        //to masquerade the drivers async buffer as a RTAI shm area
                        size = ((bufsz -1) & PAGE_MASK) + PAGE_SIZE;
			if(!rt_register(namebuf,async_buf,size,0))
				rt_printk("rt_comedi_alloc_cmd: Couldn't register memory area as RTAI shm\n");
		}
		else
			rt_printk("rt_comedi_alloc_cmd: ERROR getting subdev buffer length: %i\n", bufsz);
	}

	kcmd = rtai_kmalloc(name, sizeof(comedi_cmd) + 3*sizeof(void *) + sizeof(unsigned \
long) + chanlist_len*sizeof(unsigned int) + 16);  //kcmd->cmd.data_len = data_len;

	kcmd->name = name;
	kcmd->kcmd = kcmd;
	kcmd->kchanlist = &kcmd->name + 1;

	//TODO Allow more than one simultaneous cmd acq
	//kcmd->kdata = &kcmd->name + chanlist_len + 1;
	kcmd->kdata = async_buf;
	ofst[0] = (unsigned long)(kcmd->kchanlist - (unsigned long)kcmd);
	ofst[1] = (unsigned long)(kcmd->kdata - (unsigned long)kcmd);
	return name;
}

static unsigned long rt_comedi_free_cmd(struct rt_cmd *cmd)
{
	struct rt_cmd *kcmd;

	get_user(kcmd, &cmd->kcmd);

	rt_drg_on_name((kcmd->name)+1);

	rtai_kfree(kcmd->name);
	return kcmd->name;
}

static struct rt_fun_entry rtai_comedi_fun[] = {
	 [_KCOMEDI_OPEN]                = { 0, comedi_open }
	,[_KCOMEDI_CLOSE]               = { 0, comedi_close }
	,[_KCOMEDI_LOCK]                = { 0, comedi_lock }
	,[_KCOMEDI_UNLOCK]              = { 0, comedi_unlock }
	,[_KCOMEDI_CANCEL]              = { 0, comedi_cancel }
	,[_KCOMEDI_REGISTER_CALLBACK]   = { 0, rt_comedi_register_callback }
	,[_KCOMEDI_COMMAND]             = { 0, rt_comedi_command }
	,[_KCOMEDI_COMMAND_TEST]        = { 0, rt_comedi_command_test }
/* DEPRECATED
        ,[_KCOMEDI_TRIGGER    ]         = { 0, comedi_trigger }
*/
	,[_KCOMEDI_DATA_WRITE]          = { 0, comedi_data_write}
	,[_KCOMEDI_DATA_READ]           = { 0, comedi_data_read }
	,[_KCOMEDI_DIO_CONFIG]          = { 0, comedi_dio_config }
	,[_KCOMEDI_DIO_READ]            = { 0, comedi_dio_read }
	,[_KCOMEDI_DIO_WRITE]           = { 0, comedi_dio_write }
	,[_KCOMEDI_DIO_BITFIELD]        = { 0, comedi_dio_bitfield }
	,[_KCOMEDI_GET_N_SUBDEVICES]    = { 0, comedi_get_n_subdevices }
	,[_KCOMEDI_GET_VERSION_CODE]    = { 0, comedi_get_version_code }
	,[_KCOMEDI_GET_DRIVER_NAME]     = { 0, rt_comedi_get_driver_name }
	,[_KCOMEDI_GET_BOARD_NAME]      = { 0, rt_comedi_get_board_name }
	,[_KCOMEDI_GET_SUBDEVICE_TYPE]  = { 0, comedi_get_subdevice_type }
	,[_KCOMEDI_FIND_SUBDEVICE_TYPE] = { 0, comedi_find_subdevice_by_type }
	,[_KCOMEDI_GET_N_CHANNELS]      = { 0, comedi_get_n_channels }
	,[_KCOMEDI_GET_MAXDATA]         = { 0, comedi_get_maxdata }
	,[_KCOMEDI_GET_N_RANGES]        = { 0, comedi_get_n_ranges }
	,[_KCOMEDI_DO_INSN]             = { 0, comedi_do_insn }
/* NOT YET IMPLEMENTED
        ,[_KCOMEDI_DO_INSN_LIST]        = { 0, comedi_di_insn_list }
*/
	,[_KCOMEDI_POLL]                = { 0, comedi_poll }
/* DEPRECATED FUNCTION
        ,[_KCOMEDI_GET_RANGETYPE]       = { 0, comedi_get_rangetype }
*/
	,[_KCOMEDI_GET_SUBDEVICE_FLAGS] = { 0, comedi_get_subdevice_flags }
	,[_KCOMEDI_GET_LEN_CHANLIST]    = { 0, comedi_get_len_chanlist }
	,[_KCOMEDI_GET_KRANGE]          = { 0, comedi_get_krange }
	,[_KCOMEDI_GET_BUF_HEAD_POS]    = { 0, comedi_get_buf_head_pos }
/*
	,[_KCOMEDI_SET_USER_INT_COUNT]  = { 0, comedi_set_user_int_count }
*/
	,[_KCOMEDI_MAP]                 = { 0, comedi_map }
	,[_KCOMEDI_UNMAP]               = { 0, comedi_unmap }
	,[_KCOMEDI_WAIT]                = { UW1(2, 3), rt_comedi_wait }
	,[_KCOMEDI_WAIT_IF]             = { UW1(2, 3), rt_comedi_wait_if }
	,[_KCOMEDI_WAIT_UNTIL]          = { UW1(4, 5), rt_comedi_wait_until }
	,[_KCOMEDI_WAIT_TIMED]          = { UW1(4, 5), rt_comedi_wait_timed }
	,[_KCOMEDI_ALLOC_CMD]           = { 0, rt_comedi_alloc_cmd }
	,[_KCOMEDI_FREE_CMD]            = { 0, rt_comedi_free_cmd }
	,[_KCOMEDI_GET_BUFFER_CONTENTS] = { 0, comedi_get_buffer_contents }
	,[_KCOMEDI_MARK_BUFFER_READ]    = { 0, comedi_mark_buffer_read }
	,[_KCOMEDI_GET_BUFFER_SIZE]     = { 0, comedi_get_buffer_size }
	,[_KCOMEDI_GET_BUFFER_OFFSET]   = { 0, comedi_get_buffer_offset }
/*	
	,[_KCOMEDI_MARK_BUFFER_WRITTEN] = { 0, comedi_mark_buffer_written }
*/
	,[_KCOMEDI_MARK_WAIT_MUNGE]     = { UW1(4, 7) | UW2(6, 7) , \
rt_comedi_mark_wait_munge } };

int __rtai_comedi_init(void)
{
    if( set_rt_fun_ext_index(rtai_comedi_fun, FUN_COMEDI_LXRT_INDX) ) {
      printk("Recompile your module with a different index\n");
      return -EACCES;
    }
    return(0);
}

void __rtai_comedi_exit(void)
{
    reset_rt_fun_ext_index(rtai_comedi_fun, FUN_COMEDI_LXRT_INDX);
}

module_init(__rtai_comedi_init);
module_exit(__rtai_comedi_exit);


["rtai_comedi.h" (text/x-chdr)]

/*
 * Copyright (C) 2002 Thomas Leibner (leibner@t-online.de) (first complete writeup)
 *	       2002 David Schleef (ds@schleef.org) (COMEDI master)
 *	       2002 Lorenzo Dozio (dozio@aero.polimi.it) (made it all work)
 *	       2002 Paolo Mantegazza (mantegazza@aero.polimi.it) (hints/support)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 */


#ifndef _RTAI_COMEDI_H_
#define _RTAI_COMEDI_H_

#include <rtai_types.h>
#include <rtai_sem.h>

#define FUN_COMEDI_LXRT_INDX  9

#define _KCOMEDI_OPEN 			 0
#define _KCOMEDI_CLOSE 			 1
#define _KCOMEDI_LOCK 			 2
#define _KCOMEDI_UNLOCK 		 3
#define _KCOMEDI_CANCEL 		 4
#define _KCOMEDI_REGISTER_CALLBACK 	 5
#define _KCOMEDI_COMMAND 		 6
#define _KCOMEDI_COMMAND_TEST 		 7

/* DEPRECATED function */
#define _KCOMEDI_TRIGGER 		 8

#define _KCOMEDI_DATA_WRITE 		 9
#define _KCOMEDI_DATA_READ 		10
#define _KCOMEDI_DIO_CONFIG 		11
#define _KCOMEDI_DIO_READ 		12
#define _KCOMEDI_DIO_WRITE 		13
#define _KCOMEDI_DIO_BITFIELD 		14
#define _KCOMEDI_GET_N_SUBDEVICES 	15
#define _KCOMEDI_GET_VERSION_CODE 	16
#define _KCOMEDI_GET_DRIVER_NAME 	17
#define _KCOMEDI_GET_BOARD_NAME 	18
#define _KCOMEDI_GET_SUBDEVICE_TYPE 	19
#define _KCOMEDI_FIND_SUBDEVICE_TYPE	20
#define _KCOMEDI_GET_N_CHANNELS 	21
#define _KCOMEDI_GET_MAXDATA 		22
#define _KCOMEDI_GET_N_RANGES 		23
#define _KCOMEDI_DO_INSN 		24
#define _KCOMEDI_DO_INSN_LIST		25  // not yet in kcomedilib... (tl)
#define _KCOMEDI_POLL 			26

/* DEPRECATED function */
#define _KCOMEDI_GET_RANGETYPE 		27

/* ALPHA functions */
#define _KCOMEDI_GET_SUBDEVICE_FLAGS 	28
#define _KCOMEDI_GET_LEN_CHANLIST 	29
#define _KCOMEDI_GET_KRANGE 		30
#define _KCOMEDI_GET_BUF_HEAD_POS	31
#define _KCOMEDI_SET_USER_INT_COUNT	32
#define _KCOMEDI_MAP 			33
#define _KCOMEDI_UNMAP 			34

/* RTAI specific callbacks from kcomedi to user space */
#define _KCOMEDI_WAIT			35
#define _KCOMEDI_WAIT_IF     		36
#define _KCOMEDI_WAIT_UNTIL  		37
#define _KCOMEDI_WAIT_TIMED  		38

/* RTAI specific functions to allocate/free comedi_cmd */
#define _KCOMEDI_ALLOC_CMD  		39
#define _KCOMEDI_FREE_CMD  		40

/* buffer control functions (together with idx 31 above)*/
#define _KCOMEDI_GET_BUFFER_CONTENTS 41
#define _KCOMEDI_MARK_BUFFER_READ 42
#define _KCOMEDI_GET_BUFFER_SIZE 43
#define _KCOMEDI_GET_BUFFER_OFFSET 44
#define _KCOMEDI_MARK_BUFFER_WRITTEN 45 // not yet in kcomedilib api... (al)
#define _KCOMEDI_MARK_WAIT_MUNGE 46

#ifdef __KERNEL__ /* For kernel module build. */

#include <linux/comedilib.h>

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

int rt_comedi_register_callback(
	void *dev,
	unsigned int subdev,
	unsigned int mask,
	SEM *sem
);

unsigned int rt_comedi_wait(
	SEM *sem,
	int *semcnt
);


unsigned int rt_comedi_mark_wait_munge(
	void *dev,
	unsigned int subdev,
	int oldbytes,
	int *newbytes,
	SEM *sem,
	int *semcnt
);

unsigned int rt_comedi_wait_if(
	SEM *sem,
	int *semcnt
);

unsigned int rt_comedi_wait_until(
	SEM *sem,
	RTIME until,
	int *semcnt
);

unsigned int rt_comedi_wait_timed(
	SEM *sem,
	RTIME delay,
	int *semcnt
);

char *rt_comedi_get_driver_name(
	void *dev,
	char *name
);

char *rt_comedi_get_board_name(
	void *dev,
	char *name
);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#else  /* __KERNEL__ not defined */

#include <string.h>
#include <asm/rtai_lxrt.h>
#include <rtai_shm.h>
#include <linux/comedi.h>

#define COMEDI_LXRT_SIZARG sizeof(arg)

RTAI_PROTO(void *, comedi_open,(const char *filename))
{
	char lfilename[COMEDI_NAMELEN];
	struct { char *minor; } arg = { lfilename };
	strncpy(lfilename, filename, COMEDI_NAMELEN - 1);
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_OPEN, \
&arg).v[LOW]; }

RTAI_PROTO(int, comedi_close,(void *dev))
{
	struct { void *dev; } arg = { dev };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_CLOSE, \
&arg).i[LOW]; }

RTAI_PROTO(int, comedi_lock,(void *dev, unsigned int subdev))
{
	struct { void *dev; unsigned int subdev; } arg = { dev, subdev };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_LOCK, \
&arg).i[LOW]; }

RTAI_PROTO(int, comedi_unlock,(void *dev, unsigned int subdev))
{
	struct { void *dev; unsigned int subdev; } arg = { dev, subdev };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_UNLOCK, \
&arg).i[LOW]; }

RTAI_PROTO(int, comedi_cancel,(void *dev, unsigned int subdev))
{
	struct { void *dev; unsigned int subdev; } arg = { dev, subdev };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_CANCEL, \
&arg).i[LOW]; }

RTAI_PROTO(int, rt_comedi_register_callback,(void *dev, unsigned int subdev, unsigned \
int mask, SEM *sem)) {
	struct
	{
		void *dev; unsigned int subdev; unsigned int mask; SEM *sem;
	} arg = { dev, subdev, mask, sem };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, \
_KCOMEDI_REGISTER_CALLBACK, &arg).i[LOW]; }

#define comedi_register_callback(dev, subdev, mask, cb, arg)  \
rt_comedi_register_callback(dev, subdev, mask, arg)

RTAI_PROTO(unsigned int, rt_comedi_wait,(SEM *sem, int *semcnt))
{
	int lsemcnt;
	unsigned int retval;
	struct { SEM *sem; int *semcnt; int size; } arg = { sem, &lsemcnt, sizeof(int *) };
	retval = rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_WAIT, \
&arg).i[LOW];  if (semcnt) {
		*semcnt = lsemcnt;
	}
	return retval;
}

/**
 * allows buffer synchronisation between comedi driver and lxrt program in a single 
 * call. Can replace calls to comedi_mark_buffer_read, comedi_get_buffer_contents and \
                
 * rt_comedi_wait in an asynchronous _input_ acquisition.
 * @param dev (must be) a pointer to a comedi_t structure
 * @param subdev the number of the comedi subdevice to perform acquistion on
 * @param *newbytes reference to the number of bytes read by userspace 
 *	  before the function call and to the number of bytes written by the comedi driver
 *	  after the function call
 * @param *sem pointer to the RTAI semaphore used to synchronize comedi driver with
 *        lxrt program
 * @param *semcnt contains the semaphore count after return. 
 *	  In case of TRIG_WAKE_EOS it is increased by one for each scan
 * @return callback mask indicating transfer status (see comedi.h: "callback stuff")
 */
RTAI_PROTO(unsigned int, rt_comedi_mark_wait_munge,(void *dev, unsigned int subdev, \
int *newbytes, SEM *sem, int *semcnt)) {
	int lnewbytes, lsemcnt;
	unsigned int retval;
	if (newbytes) {
		lnewbytes = *newbytes;
    		struct { 
		    void *dev; unsigned int subdev; int oldbytes; int *newbytes; SEM *sem; int \
*semcnt; int nbsmsize;  } arg = { dev, subdev, lnewbytes, &lnewbytes, sem, &lsemcnt, \
sizeof(int *)};  retval = rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, \
_KCOMEDI_MARK_WAIT_MUNGE, &arg).i[LOW];

		*newbytes = lnewbytes;
		if (semcnt) {
		    *semcnt = lsemcnt;
		}
	}
	else {
		retval = rt_comedi_wait(sem, semcnt);
	}	
	return retval;
}

RTAI_PROTO(unsigned int, rt_comedi_wait_if,(SEM *sem, int *semcnt))
{
	int lsemcnt;
	unsigned int retval;
	struct { SEM *sem; int *semcnt; int size; } arg = { sem, &lsemcnt, sizeof(int *) };
	retval = rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_WAIT_IF, \
&arg).i[LOW];  if (semcnt) {
		*semcnt = lsemcnt;
	}
	return retval;
}

RTAI_PROTO(unsigned int, rt_comedi_wait_until,(SEM *sem, RTIME until, int *semcnt))
{
	int lsemcnt;
	unsigned int retval;
	struct { SEM *sem; RTIME until; int *semcnt; int size; } arg = { sem, until, \
&lsemcnt, sizeof(int *) };  retval = rtai_lxrt(FUN_COMEDI_LXRT_INDX, \
COMEDI_LXRT_SIZARG, _KCOMEDI_WAIT_UNTIL, &arg).i[LOW];  if (semcnt) {
		*semcnt = lsemcnt;
	}
	return retval;
}

RTAI_PROTO(unsigned int, rt_comedi_wait_timed,(SEM *sem, RTIME delay, int *semcnt))
{
	int lsemcnt;
	unsigned int retval;
	struct
	{
		SEM *sem; RTIME delay; int *semcnt; int size;
	} arg = { sem, delay, &lsemcnt, sizeof(int *) };
	retval = rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_WAIT_TIMED, \
&arg).i[LOW];  if (semcnt) {
		*semcnt = lsemcnt;
	}
	return retval;
}

RTAI_PROTO(int, comedi_command,(void *dev, comedi_cmd *cmd))
{
	struct { void *dev; comedi_cmd *cmd; } arg = { dev, cmd };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_COMMAND, \
&arg).i[LOW]; }

RTAI_PROTO(int, comedi_command_test,(void *dev, comedi_cmd *cmd))
{
	struct { void *dev; comedi_cmd *cmd; } arg = { dev, cmd };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_COMMAND_TEST, \
&arg).i[LOW]; }

RTAI_PROTO(int, comedi_trigger,(void *dev, unsigned int subdev, comedi_trig *it))
{
	return -1;
}

RTAI_PROTO(int, comedi_data_write,(void *dev, unsigned int subdev, unsigned int chan, \
unsigned int range, unsigned int aref, lsampl_t data)) {
	struct { void *dev; unsigned int subdev; unsigned int chan; unsigned int range; \
unsigned int aref; lsampl_t data; } arg = { dev, subdev, chan, range, aref, data };  \
return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_DATA_WRITE, \
&arg).i[LOW]; }

RTAI_PROTO(int, comedi_data_read,(void *dev, unsigned int subdev, unsigned int chan, \
unsigned int range, unsigned int aref, lsampl_t *data)) {
	int retval;
	lsampl_t ldata;
	struct { void *dev; unsigned int subdev; unsigned int chan; unsigned int range; \
unsigned int aref; lsampl_t *data; } arg = { dev, subdev, chan, range, aref, &ldata \
};  retval = rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_DATA_READ, \
&arg).i[LOW];  memcpy(data, &ldata, sizeof(lsampl_t));
	return retval;
}

RTAI_PROTO(int, comedi_dio_config,(void *dev, unsigned int subdev, unsigned int chan, \
unsigned int io)) {
	struct { void *dev; unsigned int subdev; unsigned int chan; unsigned int io; } arg = \
{ dev, subdev, chan, io };  return rtai_lxrt(FUN_COMEDI_LXRT_INDX, \
COMEDI_LXRT_SIZARG, _KCOMEDI_DIO_CONFIG, &arg).i[LOW]; }

RTAI_PROTO(int, comedi_dio_read,(void *dev, unsigned int subdev, unsigned int chan, \
unsigned int *val)) {
	int retval;
	unsigned int lval;
	struct { void *dev; unsigned int subdev; unsigned int chan; unsigned int *val; } arg \
= { dev, subdev, chan, &lval };  retval = rtai_lxrt(FUN_COMEDI_LXRT_INDX, \
                COMEDI_LXRT_SIZARG, _KCOMEDI_DIO_READ, &arg).i[LOW];
	*val = lval;
	return retval;
}

RTAI_PROTO(int, comedi_dio_write,(void *dev, unsigned int subdev, unsigned int chan, \
unsigned int val)) {
	struct { void *dev; unsigned int subdev; unsigned int chan; unsigned int val; } arg \
= { dev, subdev, chan, val };  return rtai_lxrt(FUN_COMEDI_LXRT_INDX, \
COMEDI_LXRT_SIZARG, _KCOMEDI_DIO_WRITE, &arg).i[LOW]; }

RTAI_PROTO(int, comedi_dio_bitfield,(void *dev, unsigned int subdev, unsigned int \
mask, unsigned int *bits)) {
	int retval;
	unsigned int lbits;
	struct { void *dev; unsigned int subdev; unsigned int mask; unsigned int *bits; } \
arg = { dev, subdev, mask, &lbits };  retval = rtai_lxrt(FUN_COMEDI_LXRT_INDX, \
                COMEDI_LXRT_SIZARG, _KCOMEDI_DIO_BITFIELD, &arg).i[LOW];
	*bits = lbits;
	return retval;
}

RTAI_PROTO(int, comedi_get_n_subdevices,(void *dev))
{
	struct { void *dev;} arg = { dev };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, \
_KCOMEDI_GET_N_SUBDEVICES, &arg).i[LOW]; }

RTAI_PROTO(int, comedi_get_version_code,(void *dev))
{
	struct { void *dev;} arg = { dev };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, \
_KCOMEDI_GET_VERSION_CODE, &arg).i[LOW]; }

RTAI_PROTO(char *, rt_comedi_get_driver_name,(void *dev, char *name))
{
	void *p;
	char lname[COMEDI_NAMELEN];
	struct { void *dev; char *name; } arg = { dev, lname };
	if ((p = rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, \
_KCOMEDI_GET_DRIVER_NAME, &arg).v[LOW])) {  strncpy(name, lname, COMEDI_NAMELEN);
		return name;
	}
	return 0;
}

RTAI_PROTO(char *, rt_comedi_get_board_name,(void *dev, char *name))
{
	void *p;
	char lname[COMEDI_NAMELEN];
	struct { void *dev; char *name; } arg = { dev, lname };
	if ((p = rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, \
_KCOMEDI_GET_BOARD_NAME, &arg).v[LOW])) {  strncpy(name, lname, COMEDI_NAMELEN);
		return name;
	}
	return 0;
}

RTAI_PROTO(int, comedi_get_subdevice_type,(void *dev, unsigned int subdev))
{
	struct { void *dev; unsigned int subdev; } arg = { dev, subdev };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, \
_KCOMEDI_GET_SUBDEVICE_TYPE, &arg).i[LOW]; }

RTAI_PROTO(int, comedi_find_subdevice_by_type,(void *dev, int type, unsigned int \
subd)) {
	struct { void *dev; int type; unsigned int subd; } arg = { dev, type, subd };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, \
_KCOMEDI_FIND_SUBDEVICE_TYPE, &arg).i[LOW]; }

RTAI_PROTO(int, comedi_get_n_channels,(void *dev, unsigned int subdev))
{
	struct { void *dev; unsigned int subdev; } arg = { dev, subdev };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_GET_N_CHANNELS, \
&arg).i[LOW]; }

RTAI_PROTO(lsampl_t, comedi_get_maxdata,(void *dev, unsigned int subdev, unsigned int \
chan)) {
	struct { void *dev; unsigned int subdev; unsigned int chan;} arg = { dev, subdev, \
chan };  return (lsampl_t)rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, \
_KCOMEDI_GET_MAXDATA, &arg).i[LOW]; }

RTAI_PROTO(int, comedi_get_n_ranges,(void *dev, unsigned int subdev, unsigned int \
chan)) {
	struct { void *dev; unsigned int subdev; unsigned int chan;} arg = { dev, subdev, \
chan };  return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, \
_KCOMEDI_GET_N_RANGES, &arg).i[LOW]; }

RTAI_PROTO(int, comedi_do_insn,(void *dev, comedi_insn *insn))
{
	int retval;
	comedi_insn linsn = *insn;
	struct { void *dev; comedi_insn *insn; } arg = { dev, &linsn };
	retval = rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_DO_INSN, \
&arg).i[LOW];  memcpy(insn, &linsn, sizeof(comedi_insn));
	return retval;
}

RTAI_PROTO(int, comedi_poll,(void *dev, unsigned int subdev))
{
	struct { void *dev; unsigned int subdev; } arg = { dev, subdev };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_POLL, \
&arg).i[LOW]; }

RTAI_PROTO(int, comedi_get_krange,(void *dev, unsigned int subdev, unsigned int chan, \
unsigned int range, comedi_krange *krange)) {
	int retval;
	comedi_krange lkrange;
	struct
	{
		void *dev; unsigned int subdev; unsigned int chan; unsigned int range; \
comedi_krange *krange;  } arg = { dev, subdev, chan, range, &lkrange };

	retval = rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_GET_KRANGE, \
&arg).i[LOW];  memcpy(krange, &lkrange, sizeof(comedi_krange));
	return retval;
}

RTAI_PROTO(unsigned int, comedi_get_buf_head_pos,(void *dev, unsigned int subdev))
{
	struct { void *dev; unsigned int subdev;} arg = { dev, subdev };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, \
_KCOMEDI_GET_BUF_HEAD_POS, &arg).i[LOW]; }

RTAI_PROTO(int, comedi_map,(void *dev, unsigned int subdev, void *ptr))
{
	int retval;
	unsigned int liptr;
	struct { void *dev; unsigned int subdevice; void *ptr; } arg = { dev, subdev, &liptr \
};  retval = rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_MAP, \
                &arg).i[LOW];
	*((void **) ptr) = (void *)liptr;
	return retval;
}

RTAI_PROTO(int, comedi_unmap,(void *dev, unsigned int subdev))
{
	int retval;
	struct { void *dev; unsigned int subdevice; } arg = { dev, subdev };
	retval = rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_UNMAP, \
&arg).i[LOW];  return retval;
}

RTAI_PROTO(int, comedi_get_len_chanlist,(void *dev, unsigned int subdev))
{
	struct { void *dev; unsigned int subdev;} arg = { dev, subdev };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, \
_KCOMEDI_GET_SUBDEVICE_FLAGS, &arg).i[LOW]; }

/**
 * allocates a RTAI SHM area for a comedi_cmd structure, a channel list, and
 * provides reference to Comedi's asynchronous DAQ transfer buffer.
 *
 * This function makes memory available for structures relevant for asynchronous
 * data acquisition. A shared memory area is allocated for the comedi_cmd
 * structure and the channel list array. Further the transfer buffer which is
 * maintained by comedi's DAQ driver is registered as a RTAI SHM segment to be
 * referenced by <em>data</em>.
 *
 * @param dev (must be) a pointer to a comedi_t structure
 * @param subdev the number of the comedi subdevice to perform acquistion on
 * @param chanlist reference to a channel list array's pointer
 * @param chanlist_len the length of the channel list (maximum is driver specific)
 * @param data reference to a pointer -- will be set to point to the comedi transfer \
                buffer
 * @return pointer to a shm area for a comedi_cmd structure.
 */
RTAI_PROTO(
	comedi_cmd *, rt_comedi_alloc_cmd,
	(
		void *dev, unsigned int subdev, unsigned int **chanlist, unsigned int chanlist_len, \
void **data  )
)
{
	unsigned long ofst[2], name;
	comedi_cmd *cmd;
	comedi_cmd *p;

	if ((int)chanlist_len>comedi_get_len_chanlist(dev,subdev))
	    return NULL;
	if ((chanlist==NULL) || (data==NULL))
	    return NULL;

	struct
	{
		void *dev; unsigned int subdev; unsigned int chanlist_len; unsigned long *ofst;
	} arg = { dev, subdev, chanlist_len, ofst };

	name = rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_ALLOC_CMD, \
&arg).i[LOW];

	cmd = p = (comedi_cmd *)rtai_malloc(name, 1);
	*chanlist = cmd->chanlist = (unsigned int *)((char *)p + ofst[0]);
	*data = rtai_malloc(name+1,1);
	cmd->data = (sampl_t*)data;
	return cmd;
}

RTAI_PROTO(void, rt_comedi_free_cmd,(void *cmd))
{
	unsigned long name;
	struct { void *cmd; } arg = { cmd };
	name = rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_FREE_CMD, \
&arg).i[LOW];  rtai_free(name+1,cmd->data);
	rtai_free(name, cmd);
}

RTAI_PROTO(int, comedi_get_buffer_contents,(void *dev, unsigned int subdev))
{
	struct { void *dev; unsigned int subdev;} arg = { dev, subdev };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, \
_KCOMEDI_GET_BUFFER_CONTENTS, &arg).i[LOW]; }

RTAI_PROTO(int, comedi_mark_buffer_read,(void *dev, unsigned int subdev, unsigned int \
num_bytes)) {
	struct { void *dev; unsigned int subdev; unsigned int num_bytes;} arg = { dev, \
subdev, num_bytes };  return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, \
_KCOMEDI_MARK_BUFFER_READ, &arg).i[LOW]; }

/* This function is not declared as a kcomdilib symbol
RTAI_PROTO(int, comedi_mark_buffer_written,(void *dev, unsigned int subdev, unsigned \
int num_bytes)) {
	struct { void *dev; unsigned int subdev; unsigned int num_bytes;} arg = { dev, \
subdev, num_bytes };  return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, \
_KCOMEDI_MARK_BUFFER_READ, &arg).i[LOW]; }
*/

RTAI_PROTO(int, comedi_get_buffer_size,(void *dev, unsigned int subdev))
{
	struct { void *dev; unsigned int subdev;} arg = { dev, subdev };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, _KCOMEDI_GET_BUFFER_SIZE, \
&arg).i[LOW]; }

RTAI_PROTO(int, comedi_get_buffer_offset,(void *dev, unsigned int subdev))
{
	struct { void *dev; unsigned int subdev;} arg = { dev, subdev };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, \
_KCOMEDI_GET_BUFFER_OFFSET, &arg).i[LOW]; }

RTAI_PROTO(unsigned int, comedi_get_subdevice_flags,(void *dev, unsigned int subdev))
{
	struct { void *dev; unsigned int subdev;} arg = { dev, subdev };
	return rtai_lxrt(FUN_COMEDI_LXRT_INDX, COMEDI_LXRT_SIZARG, \
_KCOMEDI_GET_SUBDEVICE_FLAGS, &arg).i[LOW]; }

#endif /* #ifdef __KERNEL__ */

#endif /* #ifndef _RTAI_COMEDI_H_ */


["testa.c" (text/x-csrc)]

/*
COPYRIGHT (C) 2002 Lorenzo Dozio (dozio@aero.polimi.it)

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

This library 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
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
*/


#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>

#define KEEP_STATIC_INLINE
#include <rtai_lxrt.h>
#include <rtai_comedi.h>

//using hard real-time?
#ifdef USE_HRT
#define HRT_IF_ELSE(arg1, arg2) arg1
#define HRT_IF(arg) arg
#define HRT_IF_NOT(arg)
#else
#define HRT_IF_ELSE(arg1, arg2) arg2
#define HRT_IF(arg)
#define HRT_IF_NOT(arg) arg
#endif

#define N_CHANS		4
#define N_SCANS		20
#define T_CONVERT_ARG	10000000

char *subdevice_types[]={
        "unused",
        "analog input",
        "analog output",
        "digital input",
        "digital output",
        "digital I/O",
        "counter",
        "timer",
        "memory",
        "calibration",
        "processor"
};

char *cmdtest_messages[]={
        "success",
        "invalid source",
        "source conflict",
        "invalid argument",
        "argument conflict",
        "invalid chanlist",
};

char *cmd_src(int src,char *buf)
{
        buf[0]=0;

        if(src&TRIG_NONE)strcat(buf,"none|");
        if(src&TRIG_NOW)strcat(buf,"now|");
        if(src&TRIG_FOLLOW)strcat(buf, "follow|");
        if(src&TRIG_TIME)strcat(buf, "time|");
        if(src&TRIG_TIMER)strcat(buf, "timer|");
        if(src&TRIG_COUNT)strcat(buf, "count|");
        if(src&TRIG_EXT)strcat(buf, "ext|");
        if(src&TRIG_INT)strcat(buf, "int|");
#ifdef TRIG_OTHER
        if(src&TRIG_OTHER)strcat(buf, "other|");
#endif

        if(strlen(buf)==0){
                sprintf(buf,"unknown(0x%08x)",src);
        }else{
                buf[strlen(buf)-1]=0;
        }

        return buf;
}

void dump_cmd(comedi_cmd *cmd)
{
        char buf[100];

        printf("start:      %-8s %d\n",
                cmd_src(cmd->start_src,buf),
                cmd->start_arg);

        printf("scan_begin: %-8s %d\n",
                cmd_src(cmd->scan_begin_src,buf),
                cmd->scan_begin_arg);

        printf("convert:    %-8s %d\n",
                cmd_src(cmd->convert_src,buf),
                cmd->convert_arg);

        printf("scan_end:   %-8s %d\n",
                cmd_src(cmd->scan_end_src,buf),
                cmd->scan_end_arg);

        printf("stop:       %-8s %d\n",
                cmd_src(cmd->stop_src,buf),
                cmd->stop_arg);
}

static void *dev;
static int subdev_ai = -1;
static comedi_cmd *cmd;

//this can save trouble:
void SEGVhandler(int i);

int main(int argc, char **argv)
{
	RT_TASK *comedi_task;
	int i, subdev_ao, subdev_dio, n_subdevs, nch, type;
	char name[50];
	SEM *sem;
	unsigned int *chanlist;
	int ret;
	void *buf;

	rt_set_oneshot_mode();
	start_rt_timer(0);
	mlockall(MCL_CURRENT | MCL_FUTURE);

 	if (!(comedi_task = rt_task_init(nam2num("COMEDI"), 0, 0, 0))) {
		printf("CANNOT INIT COMEDI TASK\n");
		exit(1);
	}
	
 	if (0 == (sem = rt_sem_init(nam2num("CMDSEM"), 0))) {
		printf("CANNOT INIT SEMAPHORE\n");
		exit(1);
	}

	dev = comedi_open("/dev/comedi0");
	signal(SIGSEGV,SEGVhandler);

	printf("\n OVERALL INFO:\n");
	printf("   Version code : 0x%06x\n", comedi_get_version_code(dev));
	rt_comedi_get_board_name(dev, name);
	printf("   Board name   : %s\n", name);
	rt_comedi_get_driver_name(dev, name);
	printf("   Driver name  : %s\n", name);
	printf("   Number of subdevices : %d\n", n_subdevs = comedi_get_n_subdevices(dev));

	for (i = 0; i < n_subdevs; i++) {
		printf("\n Subdevice : %d\n", i);
		type = comedi_get_subdevice_type(dev, i);
		printf(" Type : %d (%s)\n", type, subdevice_types[type]);
		printf(" Number of channels : %d\n", nch = comedi_get_n_channels(dev, i));
		if (nch != -1) {
		    printf(" Maxdata : %d\n", comedi_get_maxdata(dev, i, 0));
		    printf(" Number of ranges : %d\n", comedi_get_n_ranges(dev, i, 0));
		}
	}

	subdev_ai = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_AI, 0);

	subdev_ao = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_AO, 0);
	subdev_dio = comedi_find_subdevice_by_type(dev, COMEDI_SUBD_DIO, 0);
	
	printf("\n Asynchronous test (with comedi commands).\n");

	comedi_lock(dev, subdev_ai);

	comedi_register_callback(dev, subdev_ai, COMEDI_CB_EOS, 0, sem);
	
	cmd = 0;
	buf2 = 0;
	cmd = rt_comedi_alloc_cmd(dev, subdev_ai, &chanlist, N_CHANS, &buf);
	
	int buf_len = comedi_get_buffer_size(dev, subdev_ai);
	buf_len /=sizeof(sampl_t);
	
	printf("\n Buffer size is %i samples.\n", buf_len);

/* the subdevice that the command is sent to */
	cmd->subdev = subdev_ai;

/* flags */
	cmd->flags = TRIG_WAKE_EOS HRT_IF(| TRIG_RT);

/* start of acquisition: immediately after call to comedi_command() */
	cmd->start_src = TRIG_NOW;
	cmd->start_arg = 0;

/* event signalling the beginning of each scan: expiring TIMER */
	cmd->scan_begin_src = TRIG_TIMER;
	cmd->scan_begin_arg = N_CHANS*T_CONVERT_ARG;

/* event triggering each A/D conversion: expiring TIMER */
	cmd->convert_src = TRIG_TIMER;
	cmd->convert_arg = T_CONVERT_ARG;

/* end of each scan: after N_CHANS conversions*/
	cmd->scan_end_src = TRIG_COUNT;
	cmd->scan_end_arg = N_CHANS;

/* event signalling the end of acquisition: after N_SCANS*N_CHANS conversions*/
	cmd->stop_src = TRIG_COUNT;
	cmd->stop_arg = N_SCANS*N_CHANS;

/* the channel list */
	cmd->chanlist = chanlist;
	cmd->chanlist_len = N_CHANS;
	chanlist[0] = CR_PACK(0,0,0);
	chanlist[1] = CR_PACK(1,0,0);
	chanlist[2] = CR_PACK(0,1,0);
	chanlist[3] = CR_PACK(1,1,0);
/*	chanlist[4] = CR_PACK(0,0,0);
	chanlist[5] = CR_PACK(0,1,0);
	chanlist[6] = CR_PACK(1,0,0);
	chanlist[7] = CR_PACK(1,1,0);
*/
/*	No longer in use by Comedi drivers:
	cmd->data = buf;
	cmd->data_len = BUFSZ*sizeof(sampl_t);
*/
        printf("\n  command before testing : \n");
        dump_cmd(cmd);
        ret = comedi_command_test(dev, cmd);
        printf("\n  First test returned %d (%s)\n", ret, cmdtest_messages[ret]);
        dump_cmd(cmd);
        ret = comedi_command_test(dev, cmd);
        printf("\n  Second test returned %d (%s)\n\n", ret, cmdtest_messages[ret]);
        if (ret != 0) {
                dump_cmd(cmd);
                printf(" ERROR PREPARING COMMAND\n");
                exit(1);
        }
	printf("Press Enter to start acquisition -- or Ctrl+C to (safely) quit.\n");
	getchar();
	comedi_command(dev, cmd);

	{
	    unsigned int mask = 0, front = 0, rear = 0;
	    int new_bytes = 0, semcnt = 0, n = 0;

	    //give daq card a head start for testing semcnt's return values
	    rt_sleep(nano2count(3*N_CHANS*T_CONVERT_ARG));

HRT_IF(	    printf("Going hard real-time. Please inspect dmesg output for \
messages.\n");  rt_make_hard_real_time();
)
    	    while (!(mask&COMEDI_CB_EOA) && !(front < rear)) {

		mask = rt_comedi_mark_wait_munge(dev, subdev_ai, &new_bytes, sem, &semcnt);
		HRT_IF_ELSE(rt_printk,printf)("\nMASK: %x, SEMCNT: %d, numnewbytes: %d, # %d", \
mask, semcnt, new_bytes, rear );

		//(new_bytes % sizeof(sampl_t)) _will_ be zero (see comedi_buf_read_n_available in \
comedi/comedi/drivers.c)  front += new_bytes/sizeof(sampl_t);

		for (n = rear; n < front; n++) {
		    HRT_IF_ELSE(rt_printk,printf)(" %d",((sampl_t *)buf)[n % buf_len]);
		}
		rear = front;
	    }
	    HRT_IF(rt_make_soft_real_time();)
	    printf("\n");
	}

	comedi_cancel(dev, subdev_ai);

	rt_comedi_free_cmd(cmd);

	comedi_unlock(dev, subdev_ai);
	comedi_close(dev);

        //reset to default SIGINT, SIGSEGV handling
        signal(SIGSEGV,SIG_DFL);

	rt_sem_delete(sem);
	stop_rt_timer();
	rt_task_delete(comedi_task);

	return 0;
}
void SEGVhandler(int i)
{
    rt_make_soft_real_time();
    printf("testa SEGVhandler: ");
    if ((dev!=0)&&(subdev_ai!=-1)) {
      if ( comedi_unlock(dev,subdev_ai) == 0 ) {
        if (cmd != 0)
	  rt_comedi_free_cmd(cmd);
        if (0 == comedi_close(dev))
          printf("Unlocked, Closed, Nice.\n");
        else
          printf("Failed to close comedi device.\n");
      }
      else {
        if (comedi_cancel(dev,subdev_ai) != 0)
          printf("Failed to cancel, unlock, close.\n");
        else {
          if (cmd != 0)
	    rt_comedi_free_cmd(cmd);
          if ( comedi_unlock(dev,subdev_ai) == 0 )
            if (0 == comedi_close(dev))
              printf("Cancelled, Unlocked, Closed, Nice.\n");
            else
              printf("Failed to close comedi device.\n");
	  
        }
      }
      printf("testa crashing on ...\n");
    }
    else {
      if (dev!=0) {
        if (0 == comedi_close(dev))
          printf("Closed, Nice.\n");
        else
          printf("Failed to close comedi device.\n");
      }
      else 
        printf("Couldn't close device because I know none.\n");
    }
    signal(SIGSEGV,SIG_DFL);
    raise(i);
}


_______________________________________________
comedi mailing list
comedi@comedi.org
https://cvs.comedi.org/cgi-bin/mailman/listinfo/comedi

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic