comedilib/doc/driverwriting.xml
Ian Abbott 0404ed50dd doc/driverwriting.xml: editing Kbuild
Add a step to the instructions for integrating a new driver into comedi.
The Kbuild file needs to be edited.
2013-05-14 12:50:14 +01:00

923 lines
31 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
<!ENTITY % comedilib_entities SYSTEM "comedilib.ent">
%comedilib_entities;
]>
<section id="driverwriting">
<title>
Writing a &comedi; driver
</title>
<para>
This section explains the most important implementations aspects of
the &comedi; device drivers. It tries to give the interested device
driver writer an overview of the different steps required to write a
new device driver.
</para>
<para>
This section does <emphasis>not</emphasis> explain all implementation
details of the &comedi; software itself: &comedi; has once and for
all solved lots of boring but indispensable infrastructural things,
such as: timers, management of which drivers
are active, memory management for drivers and buffers, wrapping
of RTOS-specific interfaces, interrupt handler management, general
error handling, the <filename role="directory">/proc</filename>
interface, etc. So,
the device driver writers can concentrate on the interesting stuff:
implementing their specific interface card's DAQ functionalities.
</para>
<para>
In order to make a decent &comedi; device driver, you must
know the answers to the following questions:
<itemizedlist>
<listitem>
<para>
How does the
<link linkend="userkernelhow">communication</link> between user-space
and kernel-space work?
</para>
</listitem>
<listitem>
<para>
What functionality is provided by the
<link linkend="comedikernelgeneric">generic</link> kernel-space
&comedi; functions, and what must be provided for each
<link linkend="boardspecific">specific new driver</link>?
</para>
</listitem>
<listitem>
<para>
How to use <link linkend="drivercallbacks">DMA and interrupts</link>?
</para>
</listitem>
<listitem>
<para>
What are the addresses and meanings of all the card's registers?
</para>
<para>
This information is to be found in the so-called <quote>register level
manual</quote> of the card. Without it, coding a device driver is close
to hopeless. It is also something that &comedi; (and hence also this
handbook) cannot give any support or information for: board
manufacturers all use their own design and nomenclature.
</para>
</listitem>
</itemizedlist>
</para>
<section id="userkernelhow">
<title>
Communication user-space &mdash; kernel-space
</title>
<para>
In user-space, you interact with the functions implemented in the
Comedilib library.
Most of the device driver core of the Comedilib library is found in
<filename role="directory">lib</filename> subdirectory.
</para>
<para>
All user-space &comedi;
<link linkend="instructions">instructions</link> and
<link linkend="commandsstreaming">commands</link>
are transmitted to kernel space through a traditional
<function>ioctl</function> system call.
(See <filename>lib/ioctl.c</filename> in Comedilib.)
The user-space information command is <emphasis>encoded</emphasis> as
a number in the <function>ioctl</function> call, and decoded in the
kernel-space library. There, they are executed by their kernel-space
counterparts. This is done in the
<filename>comedi_fops.c</filename> file in the Comedi sources: the
<function>comedi_unlocked_ioctl</function> function processes the results of
the <function>ioctl</function> system call, interprets its contents,
and then calls the corresponding kernel-space
<function>do_&hellip;_ioctl</function> function(s).
For example, a &comedi;
<link linkend="instructions">instruction</link> is further processed
by the <function>do_insn_ioctl</function> function. (Which, in turn,
uses <function>parse_insn</function> for further detailed processing.)
</para>
<para>
The data corresponding to instructions and commands is transmitted
with the <function>copy_from_user</function> function;
acquisition data captured by the interface card passes the
kernel/user-space boundary with the help of a <function>copy_to_user</function>
function.
</para>
</section>
<section id="comedikernelgeneric">
<title>
Generic functionality
</title>
<para>
The major include files of the kernel-space part of &comedi; are:
<itemizedlist>
<listitem>
<para>
<filename>include/linux/comedidev.h</filename>: 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.
</para>
</listitem>
<listitem>
<para>
<filename>include/linux/comedi_rt.h</filename>:
all the real-time stuff, such as management of ISR in RTAI and
RTLinux/Free, and spinlocks for atomic sections.
</para>
</listitem>
<listitem>
<para>
<filename>include/linux/comedilib.h</filename>: the header file for
the kernel library of &comedi; (<systemitem>kcomedilib</systemitem> module).
</para>
</listitem>
</itemizedlist>
</para>
<para>
From all the relevant &comedi; device driver code that is found in the
<filename role="directory">comedi</filename> kernel module source directory,
the <emphasis role="strong">generic</emphasis> functionality is
contained in two parts:
<itemizedlist>
<listitem>
<para>
A couple of <filename>C</filename> files contain the <emphasis
role="strong">infrastructural support</emphasis>.
From these <filename>C</filename> files, it's especially the
<filename>comedi_fops.c</filename> 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.
</para>
</listitem>
<listitem>
<para>
For <emphasis role="strong">real-time</emphasis> applications,
the subdirectory <filename role="directory">kcomedilib</filename>
implements an interface in the kernel that is similar to the &comedi;
interface accessible through the
<link linkend="functionreference">user-space Comedi library</link>.
</para>
<para>
There are some differences in what is possible and/or needed
in kernel-space and in user-space, so the functionalities offered in
<filename role="directory">kcomedilib</filename> 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.
</para>
<para>
Most drivers don't make use (yet) of these real-time functionalities.
</para>
</listitem>
</itemizedlist>
</para>
<section id="driverdatastructures">
<title>
Data structures
</title>
<para>
This Section explains the generic data structures that a device driver
interacts with:
<programlisting>
typedef struct comedi_lrange_struct <link linkend="comedilrange">comedi_lrange</link>;
typedef struct comedi_subdevice_struct <link linkend="comedisubdevice">comedi_subdevice</link>;
typedef struct comedi_device_struct <link linkend="comedidevice">comedi_device</link>:
typedef struct comedi_async_struct <link linkend="comediasync">comedi_async</link>
typedef struct comedi_driver_struct <link linkend="comedidriver">comedi_driver</link>;
</programlisting>
They can be found in
<filename>include/linux/comedidev.h</filename>.
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 (<type>comedi_lrange</type>),
subdevice, and device.
</para>
<para>
Note that these kernel-space data structures have similar names as
their
<link linkend="datatypesstructures">user-space equivalents</link>, but
they have a different (kernel-side) view on the DAQ problem and a
different meaning: they encode the interaction with the
<emphasis>hardware</emphasis>, not with the <emphasis>user</emphasis>.
</para>
<para>
However, the <type><link linkend="ref-type-comedi-insn">comedi_insn</link></type>
and <type><link linkend="ref-type-comedi-cmd">comedi_cmd</link></type>
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.
</para>
<para>
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
<type><link linkend="comedidriver">comedi_driver</link></type>
that stores the device driver information that is relevant at the
operating system level, and the data structure
<type><link linkend="comediasync">comedi_async</link></type> that stores the
information about all <emphasis>asynchronous</emphasis> activities
(interrupts, callbacks and events).
</para>
<section id="comedilrange">
<title>
<type>comedi_lrange</type>
</title>
<para>
The channel information is simple, since it contains only the signal
range information:
<programlisting>
struct comedi_lrange_struct{
int length;
<link linkend="ref-type-comedi-krange">comedi_krange</link> range[GCC_ZERO_LENGTH_ARRAY];
};
</programlisting>
</para>
</section>
<section id="comedisubdevice">
<title>
<type>comedi_subdevice</type>
</title>
<para>
The subdevice is the smallest &comedi; entity that can be used for
<quote>stand-alone</quote> DAQ, so it is no surprise that it is
quite big:
<programlisting>
struct comedi_subdevice_struct{
int type;
int n_chan;
int subdev_flags;
int len_chanlist; /* maximum length of channel/gain list */
void *private;
<link linkend="comediasync">comedi_async</link> *async;
void *lock;
void *busy;
unsigned int runflags;
int io_bits;
<link linkend="ref-type-lsampl-t">lsampl_t</link> maxdata; /* if maxdata==0, use list */
<link linkend="ref-type-lsampl-t">lsampl_t</link> *maxdata_list; /* list is channel specific */
unsigned int flags;
unsigned int *flaglist;
<link linkend="comedilrange">comedi_lrange</link> *range_table;
<link linkend="comedilrange">comedi_lrange</link> **range_table_list;
unsigned int *chanlist; /* driver-owned chanlist (not used) */
int (*insn_read)(<link linkend="comedidevice">comedi_device</link> *,<link linkend="comedisubdevice">comedi_subdevice</link> *,<link linkend="ref-type-comedi-insn">comedi_insn</link> *,<link linkend="ref-type-lsampl-t">lsampl_t</link> *);
int (*insn_write)(<link linkend="comedidevice">comedi_device</link> *,<link linkend="comedisubdevice">comedi_subdevice</link> *,<link linkend="ref-type-comedi-insn">comedi_insn</link> *,<link linkend="ref-type-lsampl-t">lsampl_t</link> *);
int (*insn_bits)(<link linkend="comedidevice">comedi_device</link> *,<link linkend="comedisubdevice">comedi_subdevice</link> *,<link linkend="ref-type-comedi-insn">comedi_insn</link> *,<link linkend="ref-type-lsampl-t">lsampl_t</link> *);
int (*insn_config)(<link linkend="comedidevice">comedi_device</link> *,<link linkend="comedisubdevice">comedi_subdevice</link> *,<link linkend="ref-type-comedi-insn">comedi_insn</link> *,<link linkend="ref-type-lsampl-t">lsampl_t</link> *);
int (*do_cmd)(<link linkend="comedidevice">comedi_device</link> *,<link linkend="comedisubdevice">comedi_subdevice</link> *);
int (*do_cmdtest)(<link linkend="comedidevice">comedi_device</link> *,<link linkend="comedisubdevice">comedi_subdevice</link> *,<link linkend="ref-type-comedi-cmd">comedi_cmd</link> *);
int (*poll)(<link linkend="comedidevice">comedi_device</link> *,<link linkend="comedisubdevice">comedi_subdevice</link> *);
int (*cancel)(<link linkend="comedidevice">comedi_device</link> *,<link linkend="comedisubdevice">comedi_subdevice</link> *);
int (*buf_change)(<link linkend="comedidevice">comedi_device</link> *,<link linkend="comedisubdevice">comedi_subdevice</link> *s,unsigned long new_size);
void (*munge)(<link linkend="comedidevice">comedi_device</link> *, <link linkend="comedisubdevice">comedi_subdevice</link> *s, void *data, unsigned int num_bytes, unsigned int start_chan_index );
unsigned int state;
};
</programlisting>
The function pointers <structfield>insn_read</structfield> &hellip;
<structfield>cancel</structfield> .
offer (pointers to) the standardized
<link linkend="functionreference">user-visible API</link>
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.)
</para>
<para>
The <structfield>buf_change</structfield> and <structfield>munge</structfield>
function pointers offer functionality that is not visible to the user and for
which the device driver writer must provide a board-specific
implementation:
<function>buf_change</function> is called when a change in the
data buffer requires handling; <function>munge</function> transforms
different bit-representations of DAQ values, for example from
<emphasis>unsigned</emphasis> to <emphasis>2's complement</emphasis>.
</para>
</section>
<section id="comedidevice">
<title>
<type>comedi_device</type>
</title>
<para>
The last data structure stores the information at the
<emphasis>device</emphasis> level:
<programlisting>
struct comedi_device_struct{
int use_count;
<link linkend="comedidriver">comedi_driver</link> *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;
<link linkend="comedisubdevice">comedi_subdevice</link> *subdevices;
int options[COMEDI_NDEVCONFOPTS];
/* dumb */
int iobase;
int irq;
<link linkend="comedisubdevice">comedi_subdevice</link> *read_subdev;
wait_queue_head_t read_wait;
<link linkend="comedisubdevice">comedi_subdevice</link> *write_subdev;
wait_queue_head_t write_wait;
struct fasync_struct *async_queue;
void (*open)(<link linkend="comedidevice">comedi_device</link> *dev);
void (*close)(<link linkend="comedidevice">comedi_device</link> *dev);
};
</programlisting>
</para>
</section>
<section id="comediasync">
<title>
<type>comedi_async</type>
</title>
<para>
The following data structure contains all relevant information:
addresses and sizes of buffers, pointers to the actual data, and the
information needed for
<link linkend="drivercallbacks">event handling</link>:
<programlisting>
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 */
<link linkend="ref-type-comedi-cmd">comedi_cmd</link> cmd;
// callback stuff
unsigned int cb_mask;
int (*cb_func)(unsigned int flags,void *);
void *cb_arg;
int (*inttrig)(<link linkend="comedidevice">comedi_device</link> *dev,<link linkend="comedisubdevice">comedi_subdevice</link> *s,unsigned int x);
};
</programlisting>
</para>
</section>
<section id="comedidriver">
<title>
<type>comedi_driver</type>
</title>
<para>
<programlisting>
struct comedi_driver_struct{
struct comedi_driver_struct *next;
char *driver_name;
struct module *module;
int (*attach)(<link linkend="comedidevice">comedi_device</link> *,comedi_devconfig *);
int (*detach)(<link linkend="comedidevice">comedi_device</link> *);
/* 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;
};
</programlisting>
</para>
</section>
</section>
<section id="driversupportfunctions">
<title>
Generic driver support functions
</title>
<para>
The directory
<filename role="directory">comedi</filename> contains a large set of
support functions. Some of the most important ones are given below.
</para>
<para>
From <filename>comedi/comedi_fops.c</filename>, 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:
<programlisting>
void comedi_event(<link linkend="comedidevice">comedi_device</link> *dev,<link linkend="comedisubdevice">comedi_subdevice</link> *s,unsigned int mask);
int comedi_buf_put(<link linkend="comediasync">comedi_async</link> *async, <link linkend="ref-type-sampl-t">sampl_t</link> x);
int comedi_buf_get(<link linkend="comediasync">comedi_async</link> *async, <link linkend="ref-type-sampl-t">sampl_t</link> *x);
static int parse_insn(<link linkend="comedidevice">comedi_device</link> *dev,<link linkend="ref-type-comedi-insn">comedi_insn</link> *insn,<link linkend="ref-type-lsampl-t">lsampl_t</link> *data,void *file);
</programlisting>
The file <filename>comedi/kcomedilib/kcomedilib_main.c</filename> provides
functions to register a callback, to poll an ongoing data acquisition,
and to print an error message:
<programlisting>
int comedi_register_callback(<link linkend="ref-type-comedi-t">comedi_t</link> *d,unsigned int subdevice, unsigned int mask,int (*cb)(unsigned int,void *),void *arg);
int comedi_poll(<link linkend="ref-type-comedi-t">comedi_t</link> *d, unsigned int subdevice);
void comedi_perror(const char *message);
</programlisting>
The file <filename>comedi/rt.c</filename> provides interrupt handling
for real-time tasks (one interrupt per <emphasis>device</emphasis>!):
<programlisting>
int comedi_request_irq(unsigned irq,void (*handler)(int, void *,struct pt_regs *), unsigned long flags,const char *device,<link linkend="comedidevice">comedi_device</link> *dev_id);
void comedi_free_irq(unsigned int irq,<link linkend="comedidevice">comedi_device</link> *dev_id)
</programlisting>
</para>
</section>
</section>
<section id="boardspecific">
<title>
Board-specific functionality
</title>
<para>
The <filename role="directory">comedi/drivers</filename>
subdirectory contains
the <emphasis role="strong">board-specific</emphasis> device driver
code. Each new card must get an entry in this directory.
<emphasis role="strong">Or</emphasis>
extend the functionality of an already existing driver file if the new
card is quite similar to that implemented in an already existing
driver. For example, many of the National Instruments DAQ cards use
the same driver files.
</para>
<para>
To help device driver writers,
&comedi; provides the <quote>skeleton</quote> of a new device driver,
in the <filename>comedi/drivers/skel.c</filename> file. Before
starting to write a new driver, make sure you understand this file,
and compare it to what you find in the other already available
board-specific files in the same directory.
</para>
<para>
The first thing you notice in <filename>skel.c</filename> is the
documentation section: the &comedi; documentation is partially
generated automatically, from the information that is given in this
section. So, please comply with the structure and the keywords
provided as &comedi; standards.
</para>
<para>
The second part of the device driver contains board-specific static
data structure and defines: addresses of hardware registers; defines and
function prototypes for functionality that is only used inside of the
device driver for this board; the encoding of the types and number of
available channels; PCI information; etc.
</para>
<para>
Each driver has to register two functions which are called when you
load and unload your board's device driver (typically via a kernel
module):
<programlisting>
mydriver_attach();
mydriver_detach();
</programlisting>
In the <quote>attach</quote> function, memory is allocated for the
necessary <link linkend="driverdatastructures">data structures</link>,
all properties of a device and its subdevices are defined, and filled
in in the generic &comedi; data structures. As part of this, pointers
to the low level instructions being supported by the subdevice have to
be set, which define the basic functionality. In somewhat more detail,
the <function>mydriver_attach</function> function must:
<itemizedlist>
<listitem>
<para>
check and request the I/O port region, IRQ, DMA, and other hardware
resources. It is convenient here if you verify the existence of the
hardware and the correctness of the other information given.
Sometimes, unfortunately, this cannot be done.
</para>
</listitem>
<listitem>
<para>
allocate memory for the private data structures.
</para>
</listitem>
<listitem>
<para>
initialize the board registers and possible subdevices (timer, DMA, PCI,
hardware FIFO, etc.).
</para>
</listitem>
<listitem>
<para>
return <literal>1</literal>, indicating success. If there were any errors along the way,
you should return the appropriate (negative) error number. If an error is
returned, the <function>mydriver_detach</function> function is
called. The <function>mydriver_detach</function> function should
check any resources that may have been allocated and release them as
necessary. The &comedi; core frees
<structfield>dev->subdevices</structfield> and
<structfield>dev->private</structfield>, so this does not need to be done in
<function>mydriver_detach</function>.
</para>
</listitem>
<listitem>
<para>
If the driver has the possibility to offer asynchronous data
acquisition, you have to code an interrupt service routine, event
handling routines, and/or callback routines.
</para>
</listitem>
</itemizedlist>
Typically, you will be able to implement most of
the above-mentioned functionality by
<emphasis>cut-and-paste</emphasis> from already existing drivers. The
<function>mydriver_attach</function> function needs most of your
attention, because it must correctly define and allocate the (private
and generic) data structures that are needed for this device. That is,
each sub-device and each channel must get appropriate data fields, and
an appropriate initialization. The good news, of course, is that
&comedi; provides the data structures and the defines that fit very
well with almost all DAQ functionalities found on interface cards.
These can be found in the
<link linkend="comedikernelgeneric">header files</link> of the
<filename role="directory">include/linux/</filename>
directory.
</para>
<para>
Drivers with digital I/O subdevices should implement the following functions,
setting the function pointers in the <type>comedi_subdevice</type>:
<itemizedlist>
<listitem>
<para>
<function>insn_bits</function>: drivers set this if they have a
function that supports reading and writing multiple bits in a digital
I/O subdevice at the same time. Most (if not all) of the drivers use
this interface instead of insn_read and insn_write for DIO subdevices.
</para>
</listitem>
<listitem>
<para>
<function>insn_config</function>: implements <constant>INSN_CONFIG</constant>
instructions. Currently used for configuring the direction of digital
I/O lines, although will eventually be used for generic configuration
of drivers that is outside the scope of the currently defined &comedi;
interface.
</para>
</listitem>
</itemizedlist>
Finally, the device driver writer must implement the
<function>insn_read</function> and <function>insn_write</function> functions for
the analog channels on the card:
<itemizedlist>
<listitem>
<para>
<function>insn_read</function>: acquire the inputs on the board and
transfer them to the software buffer of the driver.
</para>
</listitem>
<listitem>
<para>
<function>insn_write</function>: transfer data from the software
buffer to the card, and execute the appropriate output conversions.
</para>
</listitem>
</itemizedlist>
In some drivers, you want to catch interrupts, and/or want to use the
<constant><link linkend="insn-inttrig">INSN_INTTRIG</link></constant>
instruction. In this
case, you must provide and register these
<link linkend="drivercallbacks">callback</link> functions.
</para>
<para>
Implementation of all of the above-mentioned functions requires
perfect knowledge about the hardware registers and addresses of the
interface card. In general, you can find
<emphasis>some</emphasis> inspiration in the already available device
drivers, but don't trust that blind
<emphasis>cut-and-paste</emphasis> will bring you far&hellip;
</para>
</section>
<section id="drivercallbacks">
<title>
Callbacks, events and interrupts
</title>
<para>
Continuous acquisition is tyically an
<emphasis>asynchronous</emphasis> activity: the function call that
has set the acquisition in motion has returned before the acquisition
has finished (or even started). So, not only the acquired data must be
sent back to the user's buffer <quote>in the background</quote>, but
various types of asynchronous <emphasis>event handling</emphasis> can
be needed during the acquisition:
<itemizedlist>
<listitem>
<para>
The <emphasis>hardware</emphasis> can generate some error or
warning events.
</para>
</listitem>
<listitem>
<para>
Normal functional interrupts are generated by the hardware, e.g.,
signalling the filling-up of the card's hardware buffer, or the end of
an acquisition <link linkend="scan">scan</link>, etc.
</para>
</listitem>
<listitem>
<para>
The device driver writer can register a driver-supplied
<quote>callback</quote> function, that is called at the end of each
hardware interrupt routine.
</para>
</listitem>
<listitem>
<para>
Another driver-supplied callback function is executed when the user
program launches an <constant><link linkend="insn-inttrig">INSN_INTTRIG</link></constant>
instruction. This event handling is executed
<emphasis>synchronously</emphasis> with the execution of the
triggering instruction.
</para>
</listitem>
</itemizedlist>
</para>
<para>
The interrupt handlers are registered through the functions mentioned
<link linkend="driversupportfunctions">before</link>
The event handling is done in the existing &comedi; drivers in
statements such as this one:
<programlisting>
<anchor id="async-events"/>
s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR
</programlisting>
It fills in the bits corresponding to particular events in the
<type><link linkend="comediasync">comedi_async</link></type> data structure.
The possible event bits are:
<itemizedlist>
<listitem>
<para>
<anchor id="comedi-cb-eoa"/>
<constant>COMEDI_CB_EOA</constant>: execute the callback at the
<quote>End-Of-Acquisition</quote> (or <quote>End-Of-Output</quote>).
</para>
</listitem>
<listitem>
<para>
<anchor id="comedi-cb-eos"/>
<constant>COMEDI_CB_EOS</constant>: execute the callback at the
<quote>End-Of-Scan</quote>.
</para>
</listitem>
<listitem>
<para>
<anchor id="comedi-cb-overflow"/>
<constant>COMEDI_CB_OVERFLOW</constant>: execute the callback when a
buffer overflow or underflow has occurred.
</para>
</listitem>
<listitem>
<para>
<anchor id="comedi-cb-error"/>
<constant>COMEDI_CB_ERROR</constant>: execute the callback at the
occurrence of an (undetermined) error.
</para>
</listitem>
</itemizedlist>
</para>
</section>
<section id="drivercaveats">
<title>
Device driver caveats
</title>
<para>
A few things to strive for when writing a new driver:
<itemizedlist>
<listitem>
<para>
Some DAQ cards consist of different <quote>layers</quote> of hardware,
which can each be given their own device driver. Examples are:
some of the National Instruments cards, that all share the same
<emphasis>Mite</emphasis> PCI driver chip; the ubiquitous parallel
port, that can be used for simple digital IO acquisitions. If your
new card has such a multi-layer design too, please take the effort to
provide drivers for each layer separately.
</para>
</listitem>
<listitem>
<para>
Your hardware driver should be functional appropriate to the resources
allocated. I.e., if the driver is fully functional when configured
with an IRQ and DMA, it should still function moderately well with
just an IRQ, or still do minor tasks without IRQ or DMA. Does your
driver really require an IRQ to do digital I/O? Maybe someone will
want to use your driver <emphasis>just</emphasis> to do digital I/O
and has no interrupts available.
</para>
</listitem>
<listitem>
<para>
Drivers are to have absolutely <emphasis role="strong">no</emphasis>
global variables (apart from read-only, constant data, or data structures
shared by all devices), mainly because the existence of global variables
immediately negates any possibility of using the driver for two
devices. The pointer <structfield>dev->private</structfield> should be used
to point to a structure containing any additional variables needed by
a driver/device combination.
</para>
</listitem>
<listitem>
<para>
Drivers should report errors and warnings via the
<function>comedi_error</function> function.
(This is <emphasis>not</emphasis> the same function as the user-space
<function><link linkend="func-ref-comedi-perror">comedi_perror</link></function> function.)
</para>
</listitem>
</itemizedlist>
</para>
</section>
<section id="integratingdriver">
<title>
Integrating the driver in the &comedi; library
</title>
<para>
For integrating new drivers in the &comedi;'s source tree the following
things have to be done:
<itemizedlist>
<listitem>
<para>
Choose a sensible name for the source code file. Let's assume here
that you call it <quote>mydriver.c</quote>
</para>
</listitem>
<listitem>
<para>
Put your new driver into <filename>comedi/drivers/mydriver.c</filename>.
</para>
</listitem>
<listitem>
<para>
Edit <filename>comedi/drivers/Makefile.am</filename> and add <literal>mydriver.ko</literal>
to the <literal>module_PROGRAMS</literal> list. Also add a line
<programlisting>
mydriver_ko_SOURCES = mydriver.c
</programlisting>
in the alphabetically appropriate place.
</para>
</listitem>
<listitem>
<para>
Edit <filename>comedi/drivers/Kbuild</filename> and a line according to
the type of driver:
<programlisting>
obj-$(COMEDI_CONFIG_PCI_MODULES) += mydriver.o # for a PCI driver
</programlisting>
<programlisting>
obj-$(COMEDI_CONFIG_PCMCIA_MODULES) += mydriver.o # for a PCMCIA driver
</programlisting>
<programlisting>
obj-$(COMEDI_CONFIG_USB_MODULES) += mydriver.o # for a USB driver
</programlisting>
<programlisting>
obj-m += mydriver.o # for other driver types
</programlisting>
</para>
</listitem>
<listitem>
<para>
Run <command>./autogen.sh</command> in the top-level comedi directory. You will
need to have (a recent version of) autoconf and automake
installed to successfully run <command>autogen.sh</command>. Afterwards, your driver will
be built along with the rest of the drivers when you run <command>make</command>.
</para>
</listitem>
<listitem>
<para>
If you want to have your driver included in the &comedi; distribution
(you <emphasis>definitely</emphasis> want to :-) ) send it to
the &comedi; mailing list
for review and integration. See the top-level <filename>README</filename>
for details of the &comedi; mailing list.)
Note your work must be licensed under terms
compatible with the GNU GPL to be distributed as a part of &comedi;.
</para>
</listitem>
</itemizedlist>
</para>
</section>
</section>