|
|
# Comedi
|
|
|
|
|
|
## Version
|
|
|
|
|
|
You can either find COMEDI in the kernel staging tree or download it
|
|
|
from its own web site. Even though both code in both places claims to be
|
|
|
version 0.7.76, the two copies are different. The website version is
|
|
|
abandoned since 2008 when it was added to the kernel staging tree. Only
|
|
|
the kernel version is updated.
|
|
|
|
|
|
One of the main improvements made in the kernel version is about the
|
|
|
creation of the char devices; with the old web site version, the end
|
|
|
user has the duty of the device files creation, either with mknod or
|
|
|
make install. During the COMEDI reviewing to insert it in the kernel
|
|
|
staging tree, developers added the automatic creation of the char device
|
|
|
with udev.
|
|
|
|
|
|
Kernel version still supports old drivers written before COMEDI become
|
|
|
part of the staging tree. Some features offered in the kernel versions
|
|
|
are absent in the other repository, so low-level drivers written for the
|
|
|
kernel version won't compile under the other one.
|
|
|
|
|
|
## Documentation
|
|
|
|
|
|
The COMEDI documentation is available on the COMEDI website
|
|
|
http://www.comedi.org/documentation.html and mainly refers to comedilib,
|
|
|
which is not especially interesting.
|
|
|
|
|
|
Reading COMEDI source code may be the only way to understand how the
|
|
|
framework works. Unfortunately, the code is poorly commented and thus it
|
|
|
is difficult; the only commented code is the COMEDI driver example
|
|
|
(`comedi/drivers/skel.c`) that tries to explains how to write a COMEDI
|
|
|
driver.
|
|
|
|
|
|
## COMEDI parameters
|
|
|
|
|
|
The COMEDI core provides three parameters that can be set at module load
|
|
|
time: `comedi_debug`, `comedi_autoconfig`, `comedi_num_legacy_minors`.
|
|
|
|
|
|
- `comedi_debug` is used to print some extra debug information. The
|
|
|
parameter is only available if you enabled CONFIG\_COMEDI\_DEBUG=y
|
|
|
at compile time. To enable debug information, load COMEDI module
|
|
|
with `comedi_debug=1`, otherwise the default of 0 applies.
|
|
|
|
|
|
<!-- end list -->
|
|
|
|
|
|
- `comedi_autoconfig` is used to enable/disable the automatic creation
|
|
|
of char devices. Auto configuration
|
|
|
is enabled by default (`comedi_autoconfig=1`) and permits to create
|
|
|
automatically the char devices files `/dev/comediN`. If auto
|
|
|
configuration is disabled (`comedi_autoconfig=0`) COMEDI creates 16
|
|
|
char devices.
|
|
|
|
|
|
<!-- end list -->
|
|
|
|
|
|
- `comedi_num_legacy_minors` is used to provide supports to old
|
|
|
drivers without auto configuration By setting this parameter COMEDI
|
|
|
create automatically the required device files `/dev/comediN`. This
|
|
|
parameter supports only values between 0 and 48 (included).
|
|
|
|
|
|
## Device organization
|
|
|
|
|
|
Comedi organizes hardware on three levels: devices, sub-devices and
|
|
|
channels. Comedi describes its hierarchy in the following way:
|
|
|
|
|
|
- **Channel** is the lowest-level hardware component, that represents
|
|
|
the properties of one single data channel; for example, an analog
|
|
|
input, or a digital output.
|
|
|
- **Sub-device** is a set of functionally identical channels that are
|
|
|
physically implemented on the same interface card. For example, a
|
|
|
set of 16 identical analogue outputs.
|
|
|
- **Device** is a set of sub-devices that are physically implemented
|
|
|
on the same interface card; in other words, the interface card
|
|
|
itself.
|
|
|
|
|
|
COMEDI allocates 256 minor numbers and uses these for devices and
|
|
|
sub-devices. The first 49 minors (from 0 to 48) are reserved for devices
|
|
|
and other ones for sub-devices. A minor is assigned to a sub-device only
|
|
|
if it supports the COMEDI command interface. The command interface is
|
|
|
used to implement streaming mode, described later.
|
|
|
|
|
|
Minor management is still inefficient when auto configuration is
|
|
|
enabled. Device may be remote over a synchronized network, so the host
|
|
|
system may really to handle hundreds of them. For example a battery of
|
|
|
49 device with:
|
|
|
|
|
|
- 2 analog inputs, with stream support
|
|
|
- 2 analog outputs, with stream support
|
|
|
|
|
|
they require 245 minors (49 for devices and 196 for sub-devices). In
|
|
|
essence, it is easy to run out of minors numbers when many devices or
|
|
|
sub-devices with streaming support are used.
|
|
|
|
|
|
## Auto configuration
|
|
|
|
|
|
Auto configuration is enabled by default and allows the automatic
|
|
|
creation of the device files `/dev/comediN` on device connection. To use
|
|
|
auto configuration, the specific driver must support it, otherwise
|
|
|
setting `comedi_autoconfig=1` won't have any effect. The COMEDI
|
|
|
framework provides two functions for auto configuration:
|
|
|
|
|
|
- `comedi_pci_auto_config()`, for PCI devices
|
|
|
- `comedi_usb_auto_config()`, for USB devices
|
|
|
|
|
|
If a COMEDI driver doesn't support auto configuration you must set the
|
|
|
parameter `comedi_num_legacy_minors`, so COMEDI creates the device files
|
|
|
`/dev/comediN` for you.
|
|
|
|
|
|
You can set both the parameters `comedi_num_legacy_minors=N` and
|
|
|
`comedi_autoconfig=1` (auto configuration enabled); in this case COMEDI
|
|
|
reserves the first N minors for drivers without auto configuration, and
|
|
|
the other 49-N for drivers with auto configuration. The device files
|
|
|
created with
|
|
|
`comedi_num_legacy_minors` are available only for drivers without auto
|
|
|
configuration support. For example if you set
|
|
|
`comedi_num_legacy_minors=10` and `comedi_autoconfig=1` you have minors
|
|
|
from 0 to 9 for drivers without auto configuration, and minors from 10
|
|
|
to 48 for drivers with auto configuration.
|
|
|
|
|
|
## Bugs and Issues
|
|
|
|
|
|
While looking at the code in version 3.1 of the Linux kernel, I noticed
|
|
|
a few bugs and suboptimal points.
|
|
|
One issue is in `comedi_read()` and `comedi_write()`, functions used for
|
|
|
data stream. The problem appears in the following lines, part of
|
|
|
`comedi/comedi_fops.c`:
|
|
|
|
|
|
if (signal_pending(current)) {
|
|
|
retval = -ERESTARTSYS;
|
|
|
break;
|
|
|
}
|
|
|
schedule();
|
|
|
|
|
|
These lines are not completely correct because `signal_pending()` should
|
|
|
be checked after sleeping and
|
|
|
not before. Checking for signals after `schedule()` is one of the basic
|
|
|
rules of driver writing, so the code here is clearly wrong, most likely
|
|
|
because of its age. My patch for this issue is:
|
|
|
|
|
|
schedule();
|
|
|
if (signal_pending(current)) {
|
|
|
retval = -ERESTARTSYS;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
When the control comes back to COMEDI process, it checks for pending
|
|
|
signals that were delivered while the process was sleeping.
|
|
|
|
|
|
COMEDI uses `mmap_count` to count how many processes are using the VMA;
|
|
|
this counting operations is buggy.
|
|
|
COMEDI increments the counter when `mmap()` is called, decrements it
|
|
|
when a process stops using the VMA.
|
|
|
The bug is between these two operations; if a process calls `fork()`
|
|
|
while holding a memory map on a comedi device, COMEDI does not increment
|
|
|
`mmap_count`, which will later be decremented twice when each of the
|
|
|
processes unmaps its memory. If this use pattern happens, `mmap_count`
|
|
|
will remain negative (non-zero) and will prevent the module to be
|
|
|
unloaded like it was still in use.
|
|
|
|
|
|
A solution to the problem is to use `mmap_count` in the `open()` and
|
|
|
`close()` functions of `vm_operations_struct`: increment on `open()`,
|
|
|
decrement on `close()`. `open()` is invoked any time a new reference to
|
|
|
the VMA is made, `close()` is invoked when a process stops to use the
|
|
|
VMA. I made a patch with this solution, submitted it to COMEDI
|
|
|
maintainers and it was accepted upstream.
|
|
|
|
|
|
A serious design problem concerns `file_operation`. COMEDI allows to
|
|
|
`open()` a char device without a low-level driver attached to it (device
|
|
|
is unusable); the device must be attached at a later time by issuing a
|
|
|
`COMEDI_DEVCONFIG` ioctl command. Because of this, any operation (and
|
|
|
system call) must check if a device is attached before start its work.
|
|
|
It is not correct approach: if there is not a device connected, then do
|
|
|
not permit any operations on it, in other words: forbid `open()` until a
|
|
|
device driver is not attached into COMEDI.
|
|
|
|
|
|
COMEDI does not declare a list of supported systems, but if you need to
|
|
|
compile it with different architectures then x86 family, you can't. For
|
|
|
example, if you try to compile COMEDI on PowerPC or ARM you get a
|
|
|
compile error in
|
|
|
`comedi/drivers.c:505`:
|
|
|
|
|
|
async->prealloc_buf = vmap(pages, n_pages, VM_MAP, PAGE_KERNEL_NOCACHE);
|
|
|
|
|
|
The error is on the page protection bits, because `PAGE_KERNEL_NOCACHE`
|
|
|
is defined only on some architectures, such as: x86, frv, sh, m32r,
|
|
|
mn10300.
|
|
|
|
|
|
A dirty work around for this issue is to add in `comedi/drivers.c` the
|
|
|
following preprocessor lines:
|
|
|
|
|
|
#ifndef PAGE_KERNEL_NOCACHE
|
|
|
#define PAGE_KERNEL_NOCACHE PAGE_KERNEL
|
|
|
#endif
|
|
|
|
|
|
The solution is dirty because on some architectures it might work
|
|
|
differently than expected.
|
|
|
|
|
|
In a recent patch by Ralf Baechle on June 23rd 2011 patch this problem
|
|
|
was solved with these lines:
|
|
|
|
|
|
#ifdef PAGE_KERNEL_NOCACHE
|
|
|
vmap(pages, n_pages, VM_MAP, PAGE_KERNEL_NOCACHE);
|
|
|
#else
|
|
|
vmap(pages, n_pages, VM_MAP, PAGE_KERNEL);
|
|
|
#endif
|
|
|
|
|
|
Architectures which provide a specific bitmask for `PAGE_KERNEL_NOCACHE`
|
|
|
will use it, other platforms will fall back to the `PAGE_KERNEL` value.
|
|
|
The patch is useless because, in the same day, he also patched
|
|
|
the `Kconfig` file to forbid compilation of COMEDI under architectures
|
|
|
without `PAGE_KERNEL_NOCACHE` definition.
|
|
|
|
|
|
COMEDI was designed more than 10 year ago. Today its code is old, some
|
|
|
choices show their age and don't respect current coding standards and
|
|
|
best practices; for example the large use of `ioctl()` and the way
|
|
|
processes are put to sleep.
|
|
|
|
|
|
## COMEDI from User Space
|
|
|
|
|
|
COMEDI is `ioctl()` driven, each operations is done through `ioctl()`,
|
|
|
except for data streaming. To use COMEDI from user space there are two
|
|
|
ways: you can write your raw sequence of `ioctl()` or you can use
|
|
|
comedilib. You must know a few COMEDI implementation details before
|
|
|
writing your long sequence of ioctl commands; moreover the resulting
|
|
|
code is needlessly long. The role of comedilib is helping developers
|
|
|
by providing higher-level functions that invoke `ioctl()` internally,
|
|
|
hiding some of the complexity.
|
|
|
|
|
|
The preferred choice between these two possibility is to use the
|
|
|
comedilib, which can be downloaded from the COMEDI web site; it is out
|
|
|
of scope for this discussion, which concentrates on kernel features.
|
|
|
|
|
|
## Comedi Instruction and Instruction List
|
|
|
|
|
|
COMEDI is internally based on so-called "instructions". A COMEDI
|
|
|
instruction is a command to perform a specific task. Instructions are
|
|
|
synchronous, so the invoking process is blocked until the requested task
|
|
|
completes. An instruction is defined by the `comedi_insn` structure:
|
|
|
|
|
|
struct COMEDI_insn {
|
|
|
unsigned int insn;
|
|
|
unsigned int n;
|
|
|
|
|
|
unsigned int __user *data;
|
|
|
unsigned int subdev;
|
|
|
unsigned int chanspec;
|
|
|
unsigned int unused[3];
|
|
|
};
|
|
|
|
|
|
Follow a rapid description of the structure, for details read the
|
|
|
official documentation.
|
|
|
|
|
|
- **insn**: its value define the operation to perform, that can be one
|
|
|
of the follow: INSN\_READ, INSN\_WRITE, INSN\_BITS, INSN\_GTOD (Get
|
|
|
Time Of Day), INSN\_WAIT, INSN\_CONFIG, INSN\_INTTRIG
|
|
|
- **n**: is the number of element in the data array
|
|
|
- **data**: buffer for read, write, configure operations. Its meaning
|
|
|
depends from insn value; for example, for read and write operations
|
|
|
data is a buffer of samples, for configuration is an array of
|
|
|
parameters.
|
|
|
- **subdev**: number of sub-device where apply the instruction
|
|
|
- **chanspec**: channel specification use 32 bits to set channel
|
|
|
number (index of the channel), channel range (voltage range) and
|
|
|
channel reference (voltage reference).
|
|
|
- **unused**: to align memory.
|
|
|
|
|
|
User side, once an instruction is ready it can be submitted to COMEDI
|
|
|
with the function `comedi_do_insn()`. If you need to perform a single
|
|
|
instruction, comedilib functions are preferred; they also hide
|
|
|
instructions complexity.
|
|
|
|
|
|
Instructions are useful when combined with instruction list, that is a
|
|
|
list of instructions. Instruction list is an array of instructions to be
|
|
|
executed in sequence, so when you need to perform a particular
|
|
|
sequence of operations you can build your own instruction list and push
|
|
|
it into COMEDI every time you need to perform that sequence. Instruction
|
|
|
list is just a vector of instruction so its structure is easy:
|
|
|
|
|
|
struct comedi_insnlist {
|
|
|
/* number of instruction */
|
|
|
unsigned int n_insns;
|
|
|
/* array of instruction */
|
|
|
struct comedi_insn __user *insns;
|
|
|
};
|
|
|
|
|
|
User side, instruction list can be submitted to COMEDI with the function
|
|
|
`comedi_do_insnlist()`.
|
|
|
|
|
|
Instructions are synchronous, then used only to perform simple task such
|
|
|
as: single acquisitions, single output and configuration; you can't do
|
|
|
data streaming with instructions.
|
|
|
|
|
|
Driver side the implementation of the instructions is left to developer
|
|
|
which can implements them if the device provides the functionality.
|
|
|
COMEDI does nothing special with instruction, it route instructions from
|
|
|
user space to the correct implement.
|
|
|
|
|
|
Instruction functions leave in `struct comedi_sudevice` and are the
|
|
|
following:
|
|
|
|
|
|
int (*insn_read) (struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *, unsigned int *);
|
|
|
int (*insn_write) (struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *, unsigned int *);
|
|
|
int (*insn_bits) (struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *, unsigned int *);
|
|
|
int (*insn_config) (struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *, unsigned int *);
|
|
|
|
|
|
respectively: to read from sub-device, to write to sub-device, to
|
|
|
read/write a set of bits of a digital sub-device, to configure a
|
|
|
sub-device.
|
|
|
|
|
|
## Comedi Data Streaming
|
|
|
|
|
|
For data streaming COMEDI provides command; it is a structure used to
|
|
|
configure target channels, and triggers which drive the acquisition. To
|
|
|
create a command the `struct comedi_cmd` structure must be filled
|
|
|
(defined in `comedi/comedi.h`).
|
|
|
|
|
|
The event fields are used to set up trigger(s) and coordinate the
|
|
|
acquisition. COMEDI command's events are not handled directly by COMEDI,
|
|
|
so their content depends from low-level drivers implementation; COMEDI
|
|
|
suggests some value to identify specific policies. The `_src` fields
|
|
|
define the trigger policies and `_arg` ones are related arguments for an
|
|
|
optional parameter; please check the COMEDI documentation for more
|
|
|
details on this.
|
|
|
|
|
|
Command permits to specify the sub-device of the streaming acquisition,
|
|
|
but the acquisition does not occur on all the sub-device channels but
|
|
|
only on the ones selected with the `chanlist` vector.
|
|
|
|
|
|
`data` is a pointer to a location memory where store data streaming on
|
|
|
input, or where device fetch data on output.
|
|
|
|
|
|
It is not possible to run another streaming operation on the same
|
|
|
sub-device, even if it involves a different group of channels
|
|
|
|
|
|
Driver side, to perform streaming acquisition the developer must
|
|
|
implements the following functions:
|
|
|
|
|
|
int (*do_cmd) (struct comedi_device *, struct comedi_subdevice *);
|
|
|
int (*do_cmdtest) (struct comedi_device *, struct comedi_subdevice *, struct comedi_cmd *);
|
|
|
|
|
|
The first function is used to perform the streaming, the second one is
|
|
|
used to test if streaming is possible. Typically `do_cmdtest()` verifies
|
|
|
if the submitted command complies the device features, for example if
|
|
|
the chosen trigger is a valid trigger for the device.
|
|
|
|
|
|
## Developing a Driver
|
|
|
|
|
|
To develop a COMEDI driver you must create and fill `comedi_driver` for
|
|
|
your driver and register it into COMEDI with `comedi_driver_register()`.
|
|
|
|
|
|
struct comedi_driver{
|
|
|
struct comedi_driver *next;
|
|
|
|
|
|
const char *driver_name;
|
|
|
struct module *module;
|
|
|
int (*attach) (struct comedi_device *,
|
|
|
struct comedi_devconfig *);
|
|
|
int (*detach) (struct comedi_device *);
|
|
|
|
|
|
/* number of elements in board_name and board_id arrays */
|
|
|
unsigned int num_names;
|
|
|
const char *const *board_name;
|
|
|
/* offset in bytes from one board name pointer to the next */
|
|
|
int offset;
|
|
|
};
|
|
|
|
|
|
Mandatory fields are `driver_name`, `module`, `attach()` and `detach()`.
|
|
|
|
|
|
The functions for input/output from/to device are specified for each
|
|
|
sub-device but it is not required to implement all of them.
|
|
|
|
|
|
The `attach()` function is used to initialize the device and sub-devices
|
|
|
of the board. The initialization is in-line, so each field of device and
|
|
|
sub-devices structure is filled in this function, also functions
|
|
|
assignment take place here. `detach()` function it reverse what is done
|
|
|
in the `attach()` one.
|
|
|
|
|
|
During the sub-devices initialization (`attach()`) you have to set
|
|
|
device and sub-devices features and set the I/O functions for each
|
|
|
sub-device which are the
|
|
|
following:
|
|
|
|
|
|
int (*insn_read) (struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *, unsigned int *);
|
|
|
int (*insn_write) (struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *, unsigned int *);
|
|
|
int (*insn_bits) (struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *, unsigned int *);
|
|
|
int (*insn_config) (struct comedi_device *, struct comedi_subdevice *, struct comedi_insn *, unsigned int *);
|
|
|
int (*do_cmd) (struct comedi_device *, struct comedi_subdevice *)
|
|
|
int (*do_cmdtest) (struct comedi_device *, struct comedi_subdevice *, struct comedi_cmd *);
|
|
|
int (*poll) (struct comedi_device *, struct comedi_subdevice *);
|
|
|
int (*cancel) (struct comedi_device *, struct comedi_subdevice *);
|
|
|
|
|
|
single sample read `insn_read()`, single sample write `insn_write()`,
|
|
|
streaming test `do_cmdtest()`,
|
|
|
streaming `do_cmd()`, read/write a set of bits `insn_bits()`, poll
|
|
|
`poll()` and abort acquisition `cancel()`.
|
|
|
|
|
|
There is no interfaces for buffers and triggers development; if you need
|
|
|
your own buffer or trigger you can develop them outside COMEDI.
|
|
|
|