The major include files of the kernel-space part of Comedi are:
include/linux/comedidev.h
: the
header file for kernel-only structures (device, subdevice, async
(i.e., buffer/event/interrupt/callback functionality for asynchronous
DAQ in a Comedi command), driver, lrange), variables, inline functions
and constants.
include/linux/comedi_rt.h
:
all the real-time stuff, such as management of ISR in RTAI and
RTLinux/Free, and spinlocks for atomic sections.
include/linux/comedilib.h
: the header file for
the kernel library of Comedi (kcomedilib
module).
From all the relevant Comedi device driver code that is found in the
comedi
kernel module source directory,
the generic functionality is
contained in two parts:
A couple of C
files contain the infrastructural support.
From these C
files, it's especially the
comedi_fops.c
file that implements what makes
Comedi into what people want to use it for: a library that has
solved 90% of the DAQ device driver efforts, once and for all.
For real-time applications,
the subdirectory kcomedilib
implements an interface in the kernel that is similar to the Comedi
interface accessible through the
user-space Comedi library.
There are some differences in what is possible and/or needed
in kernel-space and in user-space, so the functionalities offered in
kcomedilib
are not an exact copy
of the user-space library. For example, locking, interrupt handling,
real-time execution, callback handling, etc., are only available in
kernel-space.
Most drivers don't make use (yet) of these real-time functionalities.
This Section explains the generic data structures that a device driver interacts with:
typedef struct comedi_lrange_struct comedi_lrange; typedef struct comedi_subdevice_struct comedi_subdevice; typedef struct comedi_device_struct comedi_device: typedef struct comedi_async_struct comedi_async typedef struct comedi_driver_struct comedi_driver;
They can be found in
include/linux/comedidev.h
.
Most of the fields are filled in by the Comedi infrastructure, but
there are still quite a handful that your driver must provide or use.
As for the user-level Comedi, each of the hierarchical layers has
its own data structures: range (comedi_lrange),
subdevice, and device.
Note that these kernel-space data structures have similar names as their user-space equivalents, but they have a different (kernel-side) view on the DAQ problem and a different meaning: they encode the interaction with the hardware, not with the user.
However, the comedi_insn and comedi_cmd data structures are shared between user-space and kernel-space: this should come as no surprise, since these data structures contain all information that the user-space program must transfer to the kernel-space driver for each acquisition.
In addition to these data entities that are also known at the user level (device, sub-device, channel), the device driver level provides two more data structures which the application programmer doesn't get in touch with: the data structure comedi_driver that stores the device driver information that is relevant at the operating system level, and the data structure comedi_async that stores the information about all asynchronous activities (interrupts, callbacks and events).
The channel information is simple, since it contains only the signal range information:
struct comedi_lrange_struct{ int length; comedi_krange range[GCC_ZERO_LENGTH_ARRAY]; };
The subdevice is the smallest Comedi entity that can be used for “stand-alone” DAQ, so it is no surprise that it is quite big:
struct comedi_subdevice_struct{ int type; int n_chan; int subdev_flags; int len_chanlist; /* maximum length of channel/gain list */ void *private; comedi_async *async; void *lock; void *busy; unsigned int runflags; int io_bits; lsampl_t maxdata; /* if maxdata==0, use list */ lsampl_t *maxdata_list; /* list is channel specific */ unsigned int flags; unsigned int *flaglist; comedi_lrange *range_table; comedi_lrange **range_table_list; unsigned int *chanlist; /* driver-owned chanlist (not used) */ int (*insn_read)(comedi_device *,comedi_subdevice *,comedi_insn *,lsampl_t *); int (*insn_write)(comedi_device *,comedi_subdevice *,comedi_insn *,lsampl_t *); int (*insn_bits)(comedi_device *,comedi_subdevice *,comedi_insn *,lsampl_t *); int (*insn_config)(comedi_device *,comedi_subdevice *,comedi_insn *,lsampl_t *); int (*do_cmd)(comedi_device *,comedi_subdevice *); int (*do_cmdtest)(comedi_device *,comedi_subdevice *,comedi_cmd *); int (*poll)(comedi_device *,comedi_subdevice *); int (*cancel)(comedi_device *,comedi_subdevice *); int (*buf_change)(comedi_device *,comedi_subdevice *s,unsigned long new_size); void (*munge)(comedi_device *, comedi_subdevice *s, void *data, unsigned int num_bytes, unsigned int start_chan_index ); unsigned int state; };
The function pointers insn_read
…
cancel
.
offer (pointers to) the standardized
user-visible API
that every subdevice should offer; every device driver has to fill
in these functions with their board-specific implementations.
(Functionality for which Comedi provides generic functions will, by
definition, not show up in the device driver data structures.)
The buf_change
and munge
function pointers offer functionality that is not visible to the user and for
which the device driver writer must provide a board-specific
implementation:
buf_change
is called when a change in the
data buffer requires handling; munge
transforms
different bit-representations of DAQ values, for example from
unsigned to 2's complement.
The last data structure stores the information at the device level:
struct comedi_device_struct{ int use_count; comedi_driver *driver; void *private; kdev_t minor; char *board_name; const void *board_ptr; int attached; int rt; spinlock_t spinlock; int in_request_module; int n_subdevices; comedi_subdevice *subdevices; int options[COMEDI_NDEVCONFOPTS]; /* dumb */ int iobase; int irq; comedi_subdevice *read_subdev; wait_queue_head_t read_wait; comedi_subdevice *write_subdev; wait_queue_head_t write_wait; struct fasync_struct *async_queue; void (*open)(comedi_device *dev); void (*close)(comedi_device *dev); };
The following data structure contains all relevant information: addresses and sizes of buffers, pointers to the actual data, and the information needed for event handling:
struct comedi_async_struct{ void *prealloc_buf; /* pre-allocated buffer */ unsigned int prealloc_bufsz; /* buffer size, in bytes */ unsigned long *buf_page_list; /* physical address of each page */ unsigned int max_bufsize; /* maximum buffer size, bytes */ unsigned int mmap_count; /* current number of mmaps of prealloc_buf */ volatile unsigned int buf_write_count; /* byte count for writer (write completed) */ volatile unsigned int buf_write_alloc_count; /* byte count for writer (allocated for writing) */ volatile unsigned int buf_read_count; /* byte count for reader (read completed)*/ unsigned int buf_write_ptr; /* buffer marker for writer */ unsigned int buf_read_ptr; /* buffer marker for reader */ unsigned int cur_chan; /* useless channel marker for interrupt */ /* number of bytes that have been received for current scan */ unsigned int scan_progress; /* keeps track of where we are in chanlist as for munging */ unsigned int munge_chan; unsigned int events; /* events that have occurred */ comedi_cmd cmd; // callback stuff unsigned int cb_mask; int (*cb_func)(unsigned int flags,void *); void *cb_arg; int (*inttrig)(comedi_device *dev,comedi_subdevice *s,unsigned int x); };
struct comedi_driver_struct{ struct comedi_driver_struct *next; char *driver_name; struct module *module; int (*attach)(comedi_device *,comedi_devconfig *); int (*detach)(comedi_device *); /* number of elements in board_name and board_id arrays */ unsigned int num_names; void *board_name; /* offset in bytes from one board name pointer to the next */ int offset; };
The directory
comedi
contains a large set of
support functions. Some of the most important ones are given below.
From comedi/comedi_fops.c
, functions to handle the
hardware events (which also runs the registered callback function), to
get data in and out of the software data buffer, and to parse the
incoming functional requests:
void comedi_event(comedi_device *dev,comedi_subdevice *s,unsigned int mask); int comedi_buf_put(comedi_async *async, sampl_t x); int comedi_buf_get(comedi_async *async, sampl_t *x); static int parse_insn(comedi_device *dev,comedi_insn *insn,lsampl_t *data,void *file);
The file comedi/kcomedilib/kcomedilib_main.c
provides
functions to register a callback, to poll an ongoing data acquisition,
and to print an error message:
int comedi_register_callback(comedi_t *d,unsigned int subdevice, unsigned int mask,int (*cb)(unsigned int,void *),void *arg); int comedi_poll(comedi_t *d, unsigned int subdevice); void comedi_perror(const char *message);
The file comedi/rt.c
provides interrupt handling
for real-time tasks (one interrupt per device!):
int comedi_request_irq(unsigned irq,void (*handler)(int, void *,struct pt_regs *), unsigned long flags,const char *device,comedi_device *dev_id); void comedi_free_irq(unsigned int irq,comedi_device *dev_id)