I/O Package (Device Drivers)Introduction
The I/O package is designed as a general purpose framework for
supporting device drivers. This includes all classes of
drivers from simple serial to networking stacks and beyond.
Components of the I/O package, such as device drivers, are
configured into the system just like all other components.
Additionally, end users may add their own drivers to this set.
While the set of drivers (and the devices they represent) may be
considered static, they must be accessed via an opaque
“handle”. Each device in the system has a unique name and
the cyg_io_lookup() function is used to map that
name onto the handle for the device. This “hiding” of the
device implementation allows for generic, named devices, as well as
more flexibility. Also, the cyg_io_lookup()
function provides drivers the opportunity to initialize the device
when usage actually starts.
All devices have a name. The standard provided devices use names such
as “/dev/console” and
“/dev/serial0”, where the
“/dev/” prefix indicates that this is
the name of a device.
The entire I/O package API, as well as the standard
set of provided drivers, is written in C. Basic functions are provided to send data to and receive data
from a device. The details of how this is done is left to the device [class] itself.
For example, writing data to a block device like a disk drive may
have different semantics than writing to a serial port. Additional functions are provided to manipulate the state
of the driver and/or the actual device. These functions
are, by design, quite specific to the actual driver. This driver model supports layering; in other words, a device
may actually be created “on top of” another device.
For example, the “tty” (terminal-like) devices are
built on top of simple serial devices. The upper layer then has
the flexibility to add features and functions not found at the lower
layers. In this case the “tty” device provides
for line buffering and editing not available from the simple serial
drivers.
Some drivers will support visibility of the layers they depend
upon. The “tty” driver allows information about
the actual serial device to be manipulated by passing get/set
config calls that use a serial driver “key” down
to the serial driver itself. User API
All functions, except cyg_io_lookup()
require an I/O “handle”.
All functions return a value of the type Cyg_ErrNo. If an
error condition is detected, this value will be negative and the
absolute value indicates the actual error, as specified in
cyg/error/codes.h. The only other legal return
value will be ENOERR. All other function arguments
are pointers (references). This allows the drivers to pass information
efficiently, both into and out of the driver. The most striking
example of this is the “length” value passed to the read
and write functions. This parameter contains the desired length of
data on input to the function and the actual transferred length on
return.
// Lookup a device and return its handle
Cyg_ErrNo cyg_io_lookup(
const char *name,
cyg_io_handle_t *handle )
This function maps a device name onto an appropriate handle. If the
named device is not in the system, then the error
-ENOENT is returned. If the device is found, then
the handle for the device is returned by way of the handle pointer
*handle.
// Write data to a device
Cyg_ErrNo cyg_io_write(
cyg_io_handle_t handle,
const void *buf,
cyg_uint32 *len )
This function sends data to a device. The size of data to send is
contained in *len and the actual size sent will
be returned in the same place.
// Read data from a device
Cyg_ErrNo cyg_io_read(
cyg_io_handle_t handle,
void *buf,
cyg_uint32 *len )
This function receives data from a device. The desired size of data to
receive is contained in *len and the actual
size obtained will be returned in the same place.
// Get the configuration of a device
Cyg_ErrNo cyg_io_get_config(
cyg_io_handle_t handle,
cyg_uint32 key,
void *buf,
cyg_uint32 *len )
This function is used to obtain run-time configuration about a
device. The type of information retrieved is specified by the
key. The data will be returned in the given
buffer. The value of *len should contain the
amount of data requested, which must be at least as large as the size
appropriate to the selected key. The actual size of data retrieved is
placed in *len. The appropriate key values
differ for each driver and are all listed in the file
<cyg/io/config_keys.h>.
// Change the configuration of a device
Cyg_ErrNo cyg_io_set_config(
cyg_io_handle_t handle,
cyg_uint32 key,
const void *buf,
cyg_uint32 *len )
This function is used to manipulate or change the run-time
configuration of a device. The type of information is specified by the
key. The data will be obtained from the given
buffer. The value of *len should contain the
amount of data provided, which must match the size appropriate to the
selected key. The appropriate key values differ for each driver and
are all listed in the file
<cyg/io/config_keys.h>.
Serial driver details
Two different classes of serial drivers are provided as a standard
part of the eCos system. These are described as “raw
serial” (serial) and “tty-like” (tty).
Raw Serial Driver
Use the include file <cyg/io/serialio.h> for
this driver.
The raw serial driver is capable of sending
and receiving blocks of raw data to a serial device. Controls are
provided to configure the actual hardware, but there is no manipulation
of the data by this driver.
There may be many instances of this driver in a given system,
one for each serial channel. Each channel corresponds to a physical
device and there will typically be a device module created for this
purpose. The device modules themselves are configurable, allowing
specification of the actual hardware details, as well as such details
as whether the channel should be buffered by the serial driver,
etc.
Runtime Configuration
Runtime configuration is achieved by exchanging data structures with
the driver via the cyg_io_set_config() and
cyg_io_get_config() functions.
typedef struct {
cyg_serial_baud_rate_t baud;
cyg_serial_stop_bits_t stop;
cyg_serial_parity_t parity;
cyg_serial_word_length_t word_length;
cyg_uint32 flags;
} cyg_serial_info_t;
The field word_length contains the number of data bits per word
(character). This must be one of the values:
CYGNUM_SERIAL_WORD_LENGTH_5
CYGNUM_SERIAL_WORD_LENGTH_6
CYGNUM_SERIAL_WORD_LENGTH_7
CYGNUM_SERIAL_WORD_LENGTH_8
The field baud contains a baud rate selection. This must be
one of the values:
CYGNUM_SERIAL_BAUD_50
CYGNUM_SERIAL_BAUD_75
CYGNUM_SERIAL_BAUD_110
CYGNUM_SERIAL_BAUD_134_5
CYGNUM_SERIAL_BAUD_150
CYGNUM_SERIAL_BAUD_200
CYGNUM_SERIAL_BAUD_300
CYGNUM_SERIAL_BAUD_600
CYGNUM_SERIAL_BAUD_1200
CYGNUM_SERIAL_BAUD_1800
CYGNUM_SERIAL_BAUD_2400
CYGNUM_SERIAL_BAUD_3600
CYGNUM_SERIAL_BAUD_4800
CYGNUM_SERIAL_BAUD_7200
CYGNUM_SERIAL_BAUD_9600
CYGNUM_SERIAL_BAUD_14400
CYGNUM_SERIAL_BAUD_19200
CYGNUM_SERIAL_BAUD_38400
CYGNUM_SERIAL_BAUD_57600
CYGNUM_SERIAL_BAUD_115200
CYGNUM_SERIAL_BAUD_234000
The field stop contains the number of stop bits. This must be
one of the values:
CYGNUM_SERIAL_STOP_1
CYGNUM_SERIAL_STOP_1_5
CYGNUM_SERIAL_STOP_2
Note
On most hardware, a selection of 1.5 stop bits is only valid
if the word (character) length is 5.
The field parity contains the parity mode. This must be one of
the values:
CYGNUM_SERIAL_PARITY_NONE
CYGNUM_SERIAL_PARITY_EVEN
CYGNUM_SERIAL_PARITY_ODD
CYGNUM_SERIAL_PARITY_MARK
CYGNUM_SERIAL_PARITY_SPACE
The field flags is a bitmask which controls the behavior of the
serial device driver. It should be built from the values
CYG_SERIAL_FLAGS_xxx defined below:
#define CYG_SERIAL_FLAGS_RTSCTS 0x0001
If this bit is set then the port is placed in “hardware
handshake” mode. In this mode, the CTS and RTS pins control
when data is allowed to be sent/received at the port. This
bit is ignored if the hardware does not support this level of
handshake.
typedef struct {
cyg_int32 rx_bufsize;
cyg_int32 rx_count;
cyg_int32 tx_bufsize;
cyg_int32 tx_count;
} cyg_serial_buf_info_t;
The field rx_bufsize contains
the total size of the incoming data buffer. This is set to zero on
devices that do not support buffering (i.e. polled devices).The field rx_count contains the
number of bytes currently occupied in the incoming data buffer.
This is set to zero on devices that do not support buffering (i.e. polled
devices).The field tx_bufsize contains the
total size of the transmit data buffer. This is set to zero on devices
that do not support buffering (i.e. polled devices).The field tx_count contains the
number of bytes currently occupied in the transmit data buffer. This
is set to zero on devices that do not support buffering (i.e. polled
devices).API Detailscyg_io_write
cyg_io_write(handle, buf, len)
Send the data from buf to the device. The
driver maintains a buffer to hold the data. The size of the
intermediate buffer is configurable within the interface module. The
data is not modified at all while it is being buffered. On return,
*len contains the amount of characters actually
consumed .
It is possible to configure the write call to be blocking
(default) or non-blocking. Non-blocking mode requires both the configuration
option CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING
to be enabled, and the specific device to be set to non-blocking
mode for writes (see cyg_io_set_config()).
In blocking mode, the call will not return until there is space in the
buffer and the entire contents of buf have been
consumed.
In non-blocking mode, as much as possible gets consumed from
buf. If everything was consumed, the call
returns ENOERR. If only part of the
buf contents was consumed,
-EAGAIN is returned and the caller must try
again. On return, *len contains the number of characters actually
consumed .
The call can also return -EINTR if interrupted
via the cyg_io_get_config()/ABORT key.
cyg_io_read
cyg_io_read(handle, buf, len)
Receive data into the buffer, buf, from the
device. No manipulation of the data is performed before being
transferred. An interrupt driven interface module will support data
arriving when no read is pending by buffering the data in the serial
driver. Again, this buffering is completely configurable. On return,
*len contains the number of characters actually
received.
It is possible to configure the read call to be blocking (default)
or non-blocking. Non-blocking mode requires both the configuration
option CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING
to be enabled, and the specific device to be set to non-blocking
mode for reads (see cyg_io_set_config()).
In blocking mode, the call will not return until the requested
amount of data has been read.
In non-blocking mode, data waiting in the device buffer is copied to
buf, and the call returns immediately. If there
was enough data in the buffer to fulfill the request,
ENOERR is returned. If only part of the request
could be fulfilled, -EAGAIN is returned and the
caller must try again. On return, *len contains
the number of characters actually received.
The call can also return -EINTR if interrupted via
the cyg_io_get_config()/ABORT
key.
cyg_io_get_config
cyg_io_get_config(handle, key, buf, len)
This function returns current [runtime] information
about the device and/or driver. CYG_IO_GET_CONFIG_SERIAL_INFOBuf type:cyg_serial_info_tFunction:
This function retrieves the current state of the driver
and hardware. This information contains fields for
hardware baud rate, number of stop bits, and parity
mode. It also includes a set of flags that control the
port, such as hardware flow control.
CYG_IO_GET_CONFIG_SERIAL_BUFFER_INFOBuf type:cyg_serial_buf_info_tFunction:
This function retrieves the current state of the
software buffers in the serial drivers. For both
receive and transmit buffers it returns the total
buffer size and the current number of bytes occupied in
the buffer. It does not take into account any buffering
such as FIFOs or holding registers that the serial
device itself may have.
CYG_IO_GET_CONFIG_SERIAL_OUTPUT_DRAINBuf type:void *Function:
This function waits for any buffered output to
complete. This function only completes when there is no
more data remaining to be sent to the device.
CYG_IO_GET_CONFIG_SERIAL_OUTPUT_FLUSHBuf type:void *Function:
This function discards any buffered output for the
device.
CYG_IO_GET_CONFIG_SERIAL_INPUT_DRAINBuf type:void *Function: This function discards any buffered input for the
device.CYG_IO_GET_CONFIG_SERIAL_ABORTBuf type: void*Function:This function will cause any pending read or write calls on
this device to return with -EABORT.CYG_IO_GET_CONFIG_SERIAL_READ_BLOCKINGBuf type: cyg_uint32 (values 0 or 1)Function:This function will read back the blocking-mode
setting for read calls on this device. This call is only
available if the configuration option
CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING is
enabled.CYG_IO_GET_CONFIG_SERIAL_WRITE_BLOCKINGBuf type: cyg_uint32 (values 0 or 1)Function:
This function will read back the blocking-mode
setting for write calls on this device. This call is only
available if the configuration option
CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING is enabled.cyg_io_set_config
cyg_io_set_config(handle, key, buf,len)
This function is used to update or change runtime configuration
of a port. CYG_IO_SET_CONFIG_SERIAL_INFOBuf type:cyg_serial_info_tFunction:This function updates the information for the driver
and hardware. The information contains fields for
hardware baud rate, number of stop bits, and parity
mode. It also includes a set of flags that control the
port, such as hardware flow control.
CYG_IO_SET_CONFIG_SERIAL_READ_BLOCKINGBuf type: cyg_uint32 (values 0 or 1)Function:This function will set the blocking-mode for read
calls on this device. This call is only available if the
configuration option CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING
is enabled.
CYG_IO_SET_CONFIG_SERIAL_WRITE_BLOCKINGBuf type:cyg_uint32 (values 0 or 1)Function:This function will set the blocking-mode for write
calls on this device. This call is only available if the
configuration option CYGOPT_IO_SERIAL_SUPPORT_NONBLOCKING
is enabled.
TTY driver
Use the include file <cyg/io/ttyio.h> for
this driver.
This driver is built on top of the simple
serial driver and is typically used for a device that interfaces with
humans such as a terminal. It provides some minimal formatting of data
on output and allows for line-oriented editing on input.
Runtime configuration
Runtime configuration is achieved by exchanging data structures with
the driver via the cyg_io_set_config() and
cyg_io_get_config() functions.
typedef struct {
cyg_uint32 tty_out_flags;
cyg_uint32 tty_in_flags;
} cyg_tty_info_t;
The field tty_out_flags
is used to control what happens to data as it is send to the serial
port. It contains a bitmap comprised of the bits as defined by the
CYG_TTY_OUT_FLAGS_xxx values below.
#define CYG_TTY_OUT_FLAGS_CRLF 0x0001 // Map '\n' => '\r\n' on output
If this bit is set in tty_out_flags,
any occurrence of the character "\n" will
be replaced by the sequence "\r\n" before
being sent to the device.The field tty_in_flags
is used to control how data is handled as it comes from the serial
port. It contains a bitmap comprised of the bits as defined by the
CYG_TTY_IN_FLAGS_xxx values below.
#define CYG_TTY_IN_FLAGS_CR 0x0001 // Map '\r' => '\n' on input
If this bit is set in tty_in_flags, the
character "\r" (“return” or “enter” on
most keyboards) will be mapped to "\n".
#define CYG_TTY_IN_FLAGS_CRLF 0x0002 // Map '\r\n' => '\n' on input
If this bit is set in tty_in_flags, the
character sequence "\r\n" (often sent by DOS/Windows
based terminals) will be mapped to "\n".
#define CYG_TTY_IN_FLAGS_ECHO 0x0004 // Echo characters as processed
If this bit is set in tty_in_flags, characters
will be echoed back to the serial port as they are processed.
#define CYG_TTY_IN_FLAGS_BINARY 0x0008 // No input processing
If this bit is set in tty_in_flags, the
input will not be manipulated in any way before being placed in
the user’s buffer. API details
cyg_io_read(handle, buf, len)
This function is used to read data from the device. In the
default case, data is read until an end-of-line character ("\n"
or "\r") is read. Additionally, the characters are echoed
back to the [terminal] device. Minimal editing
of the input is also supported. When connecting to a remote target via GDB it is not possible
to provide console input while GDB is connected. The GDB remote
protocol does not support input. Users must disconnect from GDB
if this functionality is required.
cyg_io_write(handle, buf, len)
This function is used to send data to the device. In the default
case, the end-of-line character "\n" is replaced by the
sequence "\r\n".
cyg_io_get_config(handle, key, buf, len)
This function is used to get information about the channel’s
configuration at runtime. CYG_IO_GET_CONFIG_TTY_INFOBuf type:cyg_tty_info_tFunction:This function retrieves the current state of the
driver.
Serial driver keys (see above) may also be specified
in which case the call is passed directly to the serial
driver.
cyg_io_set_config(handle, key, buf, len)
This function is used to modify the channel’s configuration
at runtime. CYG_IO_SET_CONFIG_TTY_INFOBuf type:cyg_tty_info_tFunction: This function changes the current state of the
driver.Serial driver
keys (see above) may also be specified in which case the
call is passed directly to the serial driver. How to Write a Driver
A device driver is nothing more than a
named entity that supports the basic I/O functions - read, write, get
config, and set config. Typically a device driver also uses and
manages interrupts from the device. While the interface is generic and
device driver independent, the actual driver implementation is
completely up to the device driver designer. That said, the reason for using a device driver is to provide
access to a device from application code in as general purpose a
fashion as reasonable. Most driver writers are also concerned with
making this access as simple as possible while being as efficient
as possible. Most device drivers are concerned with the movement of information,
for example data bytes along a serial interface, or packets in a
network. In order to make the most efficient use of system resources,
interrupts are used. This will allow other application processing
to take place while the data transfers are under way, with interrupts
used to indicate when various events have occurred. For example,
a serial port typically generates an interrupt after a character
has been sent “down the wire” and the interface
is ready for another. It makes sense to allow further application
processing while the data is being sent since this can take quite
a long time. The interrupt can be used to allow the driver to send
a character as soon as the current one is complete, without any
active participation by the application code. The main building blocks for device drivers are found in the
include file: <cyg/io/devtab.h>All device drivers in eCos are described
by a device table entry, using the cyg_devtab_entry_t type.
The entry should be created using the DEVTAB_ENTRY() macro,
like this:
DEVTAB_ENTRY(l, name, dep_name, handlers, init, lookup, priv)
ArgumentslThe "C" label for this device table entry.nameThe "C" string name for the device.dep_nameFor a layered device, the "C" string name of the
device this device is built upon.handlersA pointer to the I/O function "handlers" (see below).initA function called when eCos is initialized. This
function can query the device, setup hardware, etc.lookupA function called when cyg_io_lookup() is called
for this device. privA placeholder for any device specific data
required by the driver.The interface to the driver is through the handlers field. This is a pointer to
a set of functions which implement the various cyg_io_XXX()
routines. This table is defined by the macro:
DEVIO_TABLE(l, write, read, select, get_config, set_config)
ArgumentslThe "C" label for this table of handlers.writeThe function called as a result of
cyg_io_write().readThe function called as a result of
cyg_io_read(). selectThe function called as a result of
cyg_io_select(). get_configThe function called as a result of
cyg_io_get_config().set_configThe function called as a result of
cyg_io_set_config().
When eCos is initialized (sometimes called
“boot” time), the init() function is called
for all devices in the system. The init() function is
allowed to return an error in which case the device will be placed
“off line” and all I/O requests to that device will be
considered in error.
The lookup() function is called whenever
the cyg_io_lookup() function
is called with this device name. The lookup function may cause the device
to come “on line” which would then allow I/O
operations to proceed. Future versions of the I/O system
will allow for other states, including power saving modes,
etc.
How to Write a Serial Hardware Interface DriverThe standard serial driver supplied with
eCos is structured as a hardware independent
portion and a hardware dependent interface module. To add support for
a new serial port, the user should be able to use the existing
hardware independent portion and just add their own interface driver which handles the details of the
actual device. The user should have no need to change the hardware
independent portion. The interfaces used by the serial driver and serial implementation
modules are contained in the file <cyg/io/serial.h>In the sections below we use the notation <<xx>> to
mean a module specific value, referred to as “xx” below.DevTab EntryThe interface module contains the devtab entry (or entries
if a single module supports more than one interface). This entry
should have the form:
DEVTAB_ENTRY(<<module_name>>,
<<device_name>>,
0,
&serial_devio,
<<module_init>>,
<<module_lookup>>,
&<<serial_channel>>
);
Argumentsmodule_nameThe "C" label for this devtab entrydevice_nameThe "C" string for the
device. E.g. /dev/serial0.serial_devioThe table of I/O functions. This set is defined in
the hardware independent serial driver and should be used.module_initThe module initialization function.module_lookupThe device lookup function. This function
typically sets up the device for actual use, turning on
interrupts, configuring the port, etc.serial_channelThis table (defined below) contains the interface
between the interface module and the serial driver proper.Serial Channel StructureEach serial device must have a “serial channel”.
This is a set of data which describes all operations on the device.
It also contains buffers, etc., if the device is to be buffered.
The serial channel is created by the macro:
SERIAL_CHANNEL_USING_INTERRUPTS(l, funs, dev_priv, baud,stop, parity, word_length,
flags, out_buf, out_buflen, in_buf, in_buflen)
ArgumentslThe "C" label for this structure.funsThe set of interface functions (see below).dev_privA placeholder for any device specific data for
this channel.baudThe initial baud rate value
(cyg_serial_baud_t).stopThe initial stop bits value
(cyg_serial_stop_bits_t).parityThe initial parity mode value
(cyg_serial_parity_t).word_lengthThe initial word length value
(cyg_serial_word_length_t).flagsThe initial driver flags value.out_bufPointer to the output
buffer. NULL if none required.out_buflenThe length of the output buffer.in_bufpointer to the input
buffer. NULL if none required.in_buflenThe length of the input buffer.
If either buffer length is zero, no buffering will take place
in that direction and only polled mode functions will be used.
The interface from the hardware independent driver into the
hardware interface module is contained in the funs table.
This is defined by the macro:
Serial Functions Structure
SERIAL_FUNS(l, putc, getc, set_config, start_xmit, stop_xmit)
ArgumentslThe "C" label for this structure.putcbool (*putc)(serial_channel *priv, unsigned char
c)
This function sends one character to the interface. It should
return true if the character is actually consumed. It should
return false if there is no space in the interface
getcunsigned char (*getc)(serial_channel *priv)
This function fetches one character from the interface. It will
be only called in a non-interrupt driven mode, thus it should
wait for a character by polling the device until ready.
set_configbool (*set_config)(serial_channel
*priv,cyg_serial_info_t *config)
This function is used to configure the port. It should return
true if the hardware is updated to match the desired
configuration. It should return false if the port cannot
support some parameter specified by the given
configuration. E.g. selecting 1.5 stop bits and 8 data bits is
invalid for most serial devices and should not be allowed.
start_xmitvoid (*start_xmit)(serial_channel *priv)
In interrupt mode, turn on the transmitter and allow for
transmit interrupts.
stop_xmitvoid (*stop_xmit)(serial_channel *priv)In interrupt mode, turn off the transmitter.Callbacks
The device interface module can execute functions in the
hardware independent driver via chan->callbacks.
These functions are available:
void (*serial_init)( serial_channel *chan )
This function is used to initialize the serial channel. It
is only required if the channel is being used in interrupt
mode.
void (*xmt_char)( serial_channel *chan )
This function would be called from an interrupt handler after a
transmit interrupt indicating that additional characters may be
sent. The upper driver will call the putc
function as appropriate to send more data to the device.
void (*rcv_char)( serial_channel *chan, unsigned char c )
This function is used to tell the driver that a character has arrived
at the interface. This function is typically called from the interrupt
handler.
Furthermore, if the device has a FIFO it should require the hardware
independent driver to provide block transfer functionality (driver CDL
should include "implements
CYGINT_IO_SERIAL_BLOCK_TRANSFER"). In that case, the following
functions are available as well:
xmt_req_reply_t (*data_xmt_req)(serial_channel *chan,
int space,
int* chars_avail,
unsigned char** chars)
void (*data_xmt_done)(serial_channel *chan, int chars_sent)
Instead of calling xmt_char() to get a single
character for transmission at a time, the driver should call
data_xmt_req() in a loop, requesting character
blocks for transfer. Call with a space argument of how much space
there is available in the FIFO.If the call returns true, the driver can read
chars_avail characters from
chars and copy them into the FIFO.If the call returns false, there are
no more buffered characters and the driver should continue without
filling up the FIFO.When all data has been unloaded, the
driver must call data_xmt_done().
rcv_req_reply_t (*data_rcv_req)(serial_channel *chan,
int avail,
int* space_avail,
unsigned char** space)
void (*data_rcv_done)(serial_channel *chan, int chars_rcvd)
Instead of calling rcv_char() with a single
character at a time, the driver should call
data_rcv_req() in a loop, requesting space to
unload the FIFO to. avail is the number of
characters the driver wishes to unload.If the call returns true, the driver can copy
space_avail characters to
space. If the call returns false, the input buffer is
full. It is up to the driver to decide what to do in that case
(callback functions for registering overflow are being planned for
later versions of the serial driver).
When all data has been unloaded, the driver must call
data_rcv_done().Serial testing with ser_filterRationale
Since some targets only have one serial connection, a serial testing harness
needs to be able to share the connection with GDB
(however, the test and GDB can also run on separate
lines).
The serial filter (ser_filter)
sits between the serial port and GDB and monitors
the exchange of data between GDB and the target.
Normally, no changes are made to the data.
When a test request packet is sent from the test on the target, it is
intercepted by the filter.
The filter and target then enter a loop, exchanging protocol data between
them which GDB never sees.
In the event of a timeout, or a crash on the target, the filter falls
back into its pass-through mode. If this happens due to a crash it should be
possible to start regular debugging with GDB. The
filter will stay in the pass-though mode until GDB
disconnects.
The ProtocolThe protocol commands are prefixed with an "@"
character which the serial filter is looking for. The protocol
commands include:
PINGAllows the test on the target to probe for the filter. The
filter responds with OK, while
GDB would just ignore the
command. This allows the tests to do nothing if they require the
filter and it is not present.CONFIGRequests a change of serial line configuration. Arguments
to the command specify baud rate, data bits, stop bits, and
parity. [This command is not fully implemented yet - there is no
attempt made to recover if the new configuration turns out to
cause loss of data.]BINARYRequests data to be sent from the filter to the
target. The data is checksummed, allowing errors in the transfer
to be detected. Sub-options of this command control how the
data transfer is made:NO_ECHO(serial driver receive test) Just send data from the
filter to the target. The test verifies the checksum and
PASS/FAIL depending on the result. EOP_ECHO(serial driver half-duplex receive and send test) As
NO_ECHO but the test echoes back the
data to the filter. The filter does a checksum on the
received data and sends the result to the target. The test
PASS/FAIL depending on the result of both checksum
verifications.DUPLEX_ECHO(serial driver duplex receive and send test) Smaller
packets of data are sent back and forth in a pattern that
ensures that the serial driver will be both sending and
receiving at the same time. Again, checksums are computed
and verified resulting in PASS/FAIL.
TEXT This is a test of the text translations in the TTY layer.
Requests a transfer of text data from the target to the filter
and possibly back again. The filter treats this as a binary
transfer, while the target ma be doing translations on the
data. The target provides the filter with checksums for what it
should expect to see. This test is not implemented yet.
The above commands may be extended, and new commands added, as
required to test (new) parts of the serial drivers in
eCos.
The Serial Tests
The serial tests are built as any other eCos test. After running the
make tests command, the tests can be found in
install/tests/io_serial/serial1A simple API test.serial2A simple serial send test. It writes out two strings, one
raw and one encoded as a GDB
O-packetserial3 [ requires the serial filter ]This tests the half-duplex send and receive capabilities
of the serial driver. serial4 [ requires the serial filter ]This test attempts to use a few different serial
configurations, testing the driver's configuration/setup
functionality. serial5 [ requires the serial filter ]This tests the duplex send and receive capabilities of the
serial driver. All tests should complete in less than 30 seconds.Serial Filter UsageRunning the ser_filter program with no (or wrong) arguments results in
the following output:
Usage: ser_filter [-t -S] TcpIPport SerialPort BaudRate
or: ser_filter -n [-t -S] SerialPort BaudRate
-t: Enable tracing.
-S: Output data read from serial line.
-c: Output data on console instead of via GDB.
-n: No GDB.
The normal way to use it with GDB is to start the filter:
$ ser_filter -t 9000 com1 38400
In this case, the filter will be listening on port 9000 and connect to the
target via the serial port COM1 at 38400 baud. On a UNIX
host, replace "COM1" with a device such as
"/dev/ttyS0".
The option enables tracing which will cause the
filter to describe its actions on the console.
Now start GDB with one of the tests as an
argument:
$ mips-tx39-elf-gdb -nw install/tests/io_serial/serial3Then connect to the filter:
(gdb) target remote localhost:9000
This should result in a connection in exactly the same way as if you
had connected directly to the target on the serial line.
(gdb) c
Which should result in output similar to the below:
Continuing.
INFO: <BINARY:16:1!>
PASS: <Binary test completed>
INFO: <BINARY:128:1!>
PASS: <Binary test completed>
INFO: <BINARY:256:1!>
PASS: <Binary test completed>
INFO: <BINARY:1024:1!>
PASS: <Binary test completed>
INFO: <BINARY:512:0!>
PASS: <Binary test completed>
...
PASS: <Binary test completed>
INFO: <BINARY:16384:0!>
PASS: <Binary test completed>
PASS: <serial13 test OK>
EXIT: <done>
If any of the individual tests fail the testing will terminate with a
FAIL.
With tracing enabled, you would also see the filter's status output:
The PING command sent from the target to determine the
presence of the filter:
[400 11:35:16] Dispatching command PING
[400 11:35:16] Responding with status OK
Each of the binary commands result in output similar to:
[400 11:35:16] Dispatching command BINARY
[400 11:35:16] Binary data (Size:16, Flags:1).
[400 11:35:16] Sending CRC: '170231!', len: 7.
[400 11:35:16] Reading 16 bytes from target.
[400 11:35:16] Done. in_crc 170231, out_crc 170231.
[400 11:35:16] Responding with status OK
[400 11:35:16] Received DONE from target.
This tracing output is normally sent as O-packets to GDB
which will display the tracing text. By using the
option, the tracing text can be redirected to the
console from which ser_filter was started.
A Note on Failures
A serial connection (especially when driven at a high baud rate) can garble the
transmitted data because of noise from the environment. It is not the job of
the serial driver to ensure data integrity - that is the job of protocols
layering on top of the serial driver. In the current implementation the serial tests and the serial filter are
not resilient to such data errors. This means that the test may crash or hang
(possibly without reporting a FAIL). It also
means that you should be aware of random errors - a FAIL
is not necessarily caused by a bug in the serial driver.
Ideally, the serial testing infrastructure should be able to distinguish
random errors from consistent errors - the former are most likely due to noise
in the transfer medium, while the latter are more likely to be caused by faulty
drivers. The current implementation of the infrastructure does not have this
capability.DebuggingIf a test fails, the serial filter's output may provide some hints about
what the problem is. If the option is used when starting
the filter, data received from the target is printed out:
[400 11:35:16] 0000 50 41 53 53 3a 3c 42 69 'PASS:<Bi'
[400 11:35:16] 0008 6e 61 72 79 20 74 65 73 'nary.tes'
[400 11:35:16] 0010 74 20 63 6f 6d 70 6c 65 't.comple'
[400 11:35:16] 0018 74 65 64 3e 0d 0a 49 4e 'ted>..IN'
[400 11:35:16] 0020 46 4f 3a 3c 42 49 4e 41 'FO:<BINA'
[400 11:35:16] 0028 52 59 3a 31 32 38 3a 31 'RY:128:1'
[400 11:35:16] 0030 21 3e 0d 0a 40 42 49 4e '!..@BIN'
[400 11:35:16] 0038 41 52 59 3a 31 32 38 3a 'ARY:128:'
[400 11:35:16] 0040 31 21 .. .. .. .. .. .. '1!'
In the case of an error during a testing command the data received by the
filter will be printed out, as will the data that was expected. This allows
the two data sets to be compared which may give some idea of what the problem
is.Device Driver Interface to the Kernel
This chapter describes the API that device drivers may use
to interact with the kernel and HAL. It is primarily concerned with
the control and management of interrupts and the synchronization of
ISRs, DSRs and threads.
The same API will be present in configurations where the kernel
is not present. In this case the functions will be supplied by code
acting directly on the HAL.
Interrupt ModeleCos presents a three level interrupt model to
device drivers. This consists of Interrupt Service Routines (ISRs) that are invoked
in response to a hardware interrupt; Deferred
Service Routines (DSRs) that are invoked in response to a request by
an ISR; and threads that are the clients of the driver.
Hardware interrupts are delivered with minimal intervention to an
ISR. The HAL decodes the hardware source of the interrupt and calls
the ISR of the attached interrupt object. This ISR may manipulate the
hardware but is only allowed to make a restricted set of calls on the
driver API. When it returns, an ISR may request that its DSR should be
scheduled to run.
A DSR will be run when it is safe to do so without interfering with
the scheduler. Most of the time the DSR will run immediately after the
ISR, but if the current thread is in the scheduler, it will be delayed
until the thread is finished. A DSR is allowed to make a larger set of
driver API calls, including, in particular, being able to call
cyg_drv_cond_signal() to wake up waiting
threads.
Finally, threads are able to make all API calls and in particular are
allowed to wait on mutexes and condition variables.
For a device driver to receive interrupts it must first define ISR and
DSR routines as shown below, and then call
cyg_drv_interrupt_create(). Using the handle
returned, the driver must then call
cyg_drv_interrupt_attach() to actually attach the
interrupt to the hardware vector.
SynchronizationThere are three levels of synchronization supported:
Synchronization with ISRs. This normally means disabling
interrupts to prevent the ISR running during a critical
section. In an SMP environment, this will also require the use of
a spinlock to synchronize with ISRs, DSRs or threads running on
other CPUs. This is implemented by the
cyg_drv_isr_lock() and
cyg_drv_isr_unlock() functions. This
mechanism should be used sparingly and for short periods only.
For finer grained synchronization, individual spinlocks are also
supplied.
Synchronization with DSRs. This will be implemented in the kernel
by taking the scheduler lock to prevent DSRs running during
critical sections. In non-kernel configurations it will be
implemented by non-kernel code. This is implemented by the
cyg_drv_dsr_lock() and
cyg_drv_dsr_unlock() functions. As with ISR
synchronization, this mechanism should be used sparingly. Only
DSRs and threads may use this synchronization mechanism, ISRs are
not allowed to do this.
Synchronization with threads. This is implemented with mutexes
and condition variables. Only threads may lock the mutexes and
wait on the condition variables, although DSRs may signal
condition variables.
Any data that is accessed from more than one level must be protected
against concurrent access. Data that is accessed by ISRs must be
protected with the ISR lock, or a spinlock at all times,
even in ISRs. Data that is shared between DSRs
and threads should be protected with the DSR lock. Data that is only
accessed by threads must be protected with mutexes.
SMP Support
Some eCos targets contain support for Symmetric Multi-Processing (SMP)
configurations, where more than one CPU may be present. This option
has a number of ramifications for the way in which device drivers must
be written if they are to be SMP-compatible.
Since it is possible for the ISR, DSR and thread components of a
device driver to execute on different CPUs, it is important that
SMP-compatible device drivers use the driver API routines correctly.
Synchronization between threads and DSRs continues to require that the
thread-side code use cyg_drv_dsr_lock() and
cyg_drv_dsr_unlock() to protect access to shared
data. While it is not strictly necessary for DSR code to claim the DSR
lock, since DSRs are run with it claimed already, it is good practice
to do so.
Synchronization between ISRs and DSRs or threads requires that access
to sensitive data be protected, in all places, by calls to
cyg_drv_isr_lock() and
cyg_drv_isr_unlock(). Disabling or masking
interrupts is not adequate, since the thread or DSR may be running on
a different CPU and interrupt enable/disable only work on the current
CPU.
The ISR lock, for SMP systems, not only disables local interrupts, but
also acquires a spinlock to protect against concurrent access from
other CPUs. This is necessary because ISRs are not run with the
scheduler lock claimed. Hence they can run in parallel with the other
components of the device driver.
The ISR lock provided by the driver API is just a shared spinlock that
is available for use by all drivers. If a driver needs to implement a
finer grain of locking, it can use private spinlocks, accessed via the
cyg_drv_spinlock_*() functions.
Device Driver Models
There are several ways in which device drivers
may be built. The exact model chosen will depend on the properties of
the device and the behavior desired. There are three basic models that
may be adopted.
The first model is to do all device processing in the ISR. When it is
invoked the ISR programs the device hardware directly and accesses
data to be transferred directly in memory. The ISR should also call
cyg_drv_interrupt_acknowledge(). When it is
finished it may optionally request that its DSR be invoked. The DSR
does nothing but call cyg_drv_cond_signal() to
cause a thread to be woken up. Thread level code must call
cyg_drv_isr_lock(), or
cyg_drv_interrupt_mask() to prevent ISRs running
while it manipulates shared memory.
The second model is to defer device processing to the DSR. The ISR
simply prevents further delivery of interrupts by either programming
the device, or by calling
cyg_drv_interrupt_mask(). It must then call
cyg_drv_interrupt_acknowledge() to allow other
interrupts to be delivered and then request that its DSR be
called. When the DSR runs it does the majority of the device handling,
optionally signals a condition variable to wake a thread, and finishes
by calling cyg_drv_interrupt_unmask() to re-allow
device interrupts. Thread level code uses
cyg_drv_dsr_lock() to prevent DSRs running while
it manipulates shared memory. The eCos serial device drivers use this
approach.
The third model is to defer device processing even further to a
thread. The ISR behaves exactly as in the previous model and simply
blocks and acknowledges the interrupt before request that the DSR
run. The DSR itself only calls
cyg_drv_cond_signal() to wake the thread. When
the thread awakens it performs all device processing, and has full
access to all kernel facilities while it does so. It should finish by
calling cyg_drv_interrupt_unmask() to re-allow
device interrupts. The eCos ethernet device drivers are written to
this model.
The first model is good for devices that need immediate processing and
interact infrequently with thread level. The second model trades a
little latency in dealing with the device for a less intrusive
synchronization mechanism. The last model allows device processing to
be scheduled with other threads and permits more complex device
handling.
Synchronization Levels
Since it would be dangerous for an ISR or DSR to make a call
that might reschedule the current thread (by trying to lock a mutex
for example) all functions in this API have an associated synchronization
level. These levels are:
Thread
This function may only be called from within threads. This is
usually the client code that makes calls into the device driver.
In a non-kernel configuration, this will be code running at the
default non-interrupt level.
DSR
This function may be called by either DSR or thread code.
ISR
This function may be called from ISR, DSR or thread code.
The following table shows, for each API function, the levels
at which is may be called:
Callable from:
Function ISR DSR Thread
-------------------------------------------------------------------------
cyg_drv_isr_lock X X X
cyg_drv_isr_unlock X X X
cyg_drv_spinlock_init X
cyg_drv_spinlock_destroy X
cyg_drv_spinlock_spin X X X
cyg_drv_spinlock_clear X X X
cyg_drv_spinlock_try X X X
cyg_drv_spinlock_test X X X
cyg_drv_spinlock_spin_intsave X X X
cyg_drv_spinlock_clear_intsave X X X
cyg_drv_dsr_lock X X
cyg_drv_dsr_unlock X X
cyg_drv_mutex_init X
cyg_drv_mutex_destroy X
cyg_drv_mutex_lock X
cyg_drv_mutex_trylock X
cyg_drv_mutex_unlock X
cyg_drv_mutex_release X
cyg_drv_cond_init X
cyg_drv_cond_destroy X
cyg_drv_cond_wait X
cyg_drv_cond_signal X X
cyg_drv_cond_broadcast X X
cyg_drv_interrupt_create X
cyg_drv_interrupt_delete X
cyg_drv_interrupt_attach X X X
cyg_drv_interrupt_detach X X X
cyg_drv_interrupt_mask X X X
cyg_drv_interrupt_unmask X X X
cyg_drv_interrupt_acknowledge X X X
cyg_drv_interrupt_configure X X X
cyg_drv_interrupt_level X X X
cyg_drv_interrupt_set_cpu X X X
cyg_drv_interrupt_get_cpu X X X
The API
This section details the Driver Kernel
Interface. Note that most of these functions are identical to Kernel C
API calls, and will in most configurations be wrappers for them. In
non-kernel configurations they will be supported directly by the HAL,
or by code to emulate the required behavior.
This API is defined in the header file
<cyg/hal/drv_api.h>.
cyg_drv_isr_lockFunction:void cyg_drv_isr_lock()Arguments:NoneResult:None Level:ISRDescription:
Disables delivery of interrupts, preventing all ISRs running. This
function maintains a counter of the number of times it is
called.
cyg_drv_isr_unlockFunction: void cyg_drv_isr_unlock()Arguments:NoneResult: None Level: ISRDescription: Re-enables delivery of interrupts, allowing ISRs to
run. This function decrements the counter maintained by
cyg_drv_isr_lock(), and only re-allows
interrupts when it goes to zero. cyg_drv_spinlock_initFunction:
void cyg_drv_spinlock_init(cyg_spinlock_t *lock, cyg_bool_t locked )
Arguments:lock - pointer to spinlock to initializelocked - initial state of lockResult:NoneLevel:ThreadDescription:
Initialize a spinlock. The locked
argument indicates how the spinlock should be initialized:
TRUE for locked or FALSE
for unlocked state.
cyg_drv_spinlock_destroyFunction:void cyg_drv_spinlock_destroy(cyg_spinlock_t *lock )Arguments:lock - pointer to spinlock destroyResult:NoneLevel:ThreadDescription:
Destroy a spinlock that is no longer of use. There should be no
CPUs attempting to claim the lock at the time this function is
called, otherwise the behavior is undefined.
cyg_drv_spinlock_spinFunction:void cyg_drv_spinlock_spin(cyg_spinlock_t *lock )Arguments:lock - pointer to spinlock to claimResult:NoneLevel:ISRDescription:
Claim a spinlock, waiting in a busy loop until it is
available. Wherever this is called from, this operation
effectively pauses the CPU until it succeeds. This operations
should therefore be used sparingly, and in situations where
deadlocks/livelocks cannot occur. Also see
cyg_drv_spinlock_spin_intsave().
cyg_drv_spinlock_clearFunction:void cyg_drv_spinlock_clear(cyg_spinlock_t *lock )Arguments:lock - pointer to spinlock to clear Result:NoneLevel:ISRDescription:
Clear a spinlock. This clears the spinlock and allows another
CPU to claim it. If there is more than one CPU waiting in
cyg_drv_spinlock_spin() then just one of
them will be allowed to proceed.
cyg_drv_spinlock_tryFunction:cyg_bool_t cyg_drv_spinlock_try(cyg_spinlock_t *lock )Arguments:lock - pointer to spinlock to tryResult:TRUE if the spinlock was claimed,
FALSE otherwise.Level:ISRDescription:
Try to claim the spinlock without waiting. If the spinlock could
be claimed immediately then TRUE is
returned. If the spinlock is already claimed then the result is
FALSE.
cyg_drv_spinlock_testFunction:cyg_bool_t cyg_drv_spinlock_test(cyg_spinlock_t *lock )Arguments:lock - pointer to spinlock to testResult:TRUE if the spinlock is available,
FALSE otherwise.Level:ISRDescription:
Inspect the state of the spinlock. If the spinlock is not locked
then the result is TRUE. If it is locked then
the result will be FALSE.
cyg_drv_spinlock_spin_intsaveFunction:
void cyg_drv_spinlock_spin_intsave(cyg_spinlock_t *lock,
cyg_addrword_t *istate )
Arguments:lock - pointer to spinlock to claimistate - pointer to interrupt state save locationResult:NoneLevel:ISRDescription:
This function behaves exactly like
cyg_drv_spinlock_spin() except that it also
disables interrupts before attempting to claim the lock. The
current interrupt enable state is saved in
*istate. Interrupts remain disabled once
the spinlock had been claimed and must be restored by calling
cyg_drv_spinlock_clear_intsave().
In general, device drivers should use this function to claim and
release spinlocks rather than the
non-_intsave() variants, to ensure proper
exclusion with code running on both other CPUs and this CPU.
cyg_drv_spinlock_clear_intsaveFunction:
void cyg_drv_spinlock_clear_intsave( cyg_spinlock_t *lock,
cyg_addrword_t istate )
Arguments:lock - pointer to spinlock to clear istate - interrupt state to restore Result:NoneLevel:ISRDescription:
This function behaves exactly like
cyg_drv_spinlock_clear() except that it
also restores an interrupt state saved by
cyg_drv_spinlock_spin_intsave(). The
istate argument must have been
initialized by a previous call to
cyg_drv_spinlock_spin_intsave().
cyg_drv_dsr_lockFunction:void cyg_drv_dsr_lock()Arguments:NoneResult: None Level: DSRDescription: Disables scheduling of DSRs. This function maintains a
counter of the number of times it has been called. cyg_drv_dsr_unlockFunction: void cyg_drv_dsr_unlock()Arguments:NoneResult:None Level:DSRDescription: Re-enables scheduling of DSRs. This function decrements
the counter incremented by
cyg_drv_dsr_lock(). DSRs are only allowed
to be delivered when the counter goes to zero. cyg_drv_mutex_initFunction: void cyg_drv_mutex_init(cyg_drv_mutex_t *mutex)Arguments:mutex - pointer to mutex to initializeResult: None Level: ThreadDescription: Initialize the mutex pointed to by the
mutex argument. cyg_drv_mutex_destroyFunction: void cyg_drv_mutex_destroy( cyg_drv_mutex_t *mutex )Arguments:mutex - pointer to mutex to destroyResult: None Level: ThreadDescription: Destroy the mutex pointed to by the
mutex argument. The mutex should be unlocked
and there should be no threads waiting to lock it when this call
in made.cyg_drv_mutex_lockFunction: cyg_bool cyg_drv_mutex_lock( cyg_drv_mutex_t *mutex )Arguments:mutex - pointer to mutex to lockResult:TRUE it the thread has claimed the
lock, FALSE otherwise.Level:ThreadDescription: Attempt to lock the mutex pointed to by the
mutex argument. If the mutex is already
locked by another thread then this thread will wait until that
thread is finished. If the result from this function is
FALSE then the thread was broken out of its
wait by some other thread. In this case the mutex will not have
been locked. cyg_drv_mutex_trylockFunction:cyg_bool cyg_drv_mutex_trylock( cyg_drv_mutex_t *mutex )Arguments:mutex - pointer to mutex to lockResult:TRUE if the mutex has been locked,
FALSE otherwise. Level:ThreadDescription:Attempt to lock the mutex pointed to by the
mutex argument without waiting. If the
mutex is already locked by some other thread then this function
returns FALSE. If the function can lock the
mutex without waiting, then TRUE is
returned. cyg_drv_mutex_unlockFunction: void cyg_drv_mutex_unlock( cyg_drv_mutex_t *mutex )Arguments:mutex - pointer to mutex to unlockResult: None Level: ThreadDescription: Unlock the mutex pointed to by the
mutex argument. If there are any threads
waiting to claim the lock, one of them is woken up to try and
claim it. cyg_drv_mutex_releaseFunction: void cyg_drv_mutex_release( cyg_drv_mutex_t *mutex )Arguments:mutex - pointer to mutex to releaseResult: None Level: ThreadDescription: Release all threads waiting on the mutex pointed to by the
mutex argument. These threads will return
from cyg_drv_mutex_lock() with a
FALSE result and will not have claimed the
mutex. This function has no effect on any thread that may have
the mutex claimed. cyg_drv_cond_initFunction: void cyg_drv_cond_init( cyg_drv_cond_t *cond, cyg_drv_mutex_t *mutex )
Arguments:cond - condition variable to initializemutex - mutex to associate with this condition variableResult: NoneLevel: Thread Description: Initialize the condition variable pointed to by the
cond argument. The
mutex argument must point to a mutex with
which this condition variable is associated. A thread may only
wait on this condition variable when it has already locked the
associated mutex. Waiting will cause the mutex to be unlocked,
and when the thread is reawakened, it will automatically claim
the mutex before continuing. cyg_drv_cond_destroyFunction: void cyg_drv_cond_destroy( cyg_drv_cond_t *cond )Arguments:cond - condition variable to destroyResult: None Level: ThreadDescription: Destroy the condition variable pointed to by the
cond argument. cyg_drv_cond_waitFunction: void cyg_drv_cond_wait( cyg_drv_cond_t *cond )Arguments:cond - condition variable to wait onResult: None Level: ThreadDescription: Wait for a signal on the condition variable pointed to by
the cond argument. The thread must have
locked the associated mutex, supplied in
cyg_drv_cond_init(), before waiting on this
condition variable. While the thread waits, the mutex will be
unlocked, and will be re-locked before this function returns. It
is possible for threads waiting on a condition variable to
occasionally wake up spuriously. For this reason it is necessary
to use this function in a loop that re-tests the condition each
time it returns. Note that this function performs an implicit
scheduler unlock/relock sequence, so that it may be used within
an explicit
cyg_drv_dsr_lock()...cyg_drv_dsr_unlock()
structure.cyg_drv_cond_signalFunction: void cyg_drv_cond_signal( cyg_drv_cond_t *cond )Arguments:cond - condition variable to signalResult: None Level: DSRDescription: Signal the condition variable pointed to by the cond
argument. If there are any threads waiting on this variable at
least one of them will be awakened. Note that in some
configurations there may not be any difference between this
function and cyg_drv_cond_broadcast().
cyg_drv_cond_broadcastFunction: void cyg_drv_cond_broadcast( cyg_drv_cond_t *cond )Arguments:cond - condition variable to broadcast toResult: None Level: DSRDescription: Signal the condition variable pointed to by the
cond argument. If there are any threads
waiting on this variable they will all be awakened. cyg_drv_interrupt_createFunction:
void cyg_drv_interrupt_create( cyg_vector_t vector,
cyg_priority_t priority,
cyg_addrword_t data,
cyg_ISR_t *isr,
cyg_DSR_t *dsr,
cyg_handle_t *handle,
cyg_interrupt *intr
)
Arguments:vector - vector to attach topriority - queuing prioritydata - data pointerisr - interrupt service routinedsr - deferred service routinehandle - returned handleintr - put interrupt object hereResult: NoneLevel: ThreadDescription: Create an interrupt object and returns a handle to it. The
object contains information about which interrupt vector to use
and the ISR and DSR that will be called after the interrupt
object is attached to the vector. The interrupt object will be
allocated in the memory passed in the
intr parameter. The interrupt object is
not immediately attached; it must be attached with the
cyg_interrupt_attach() call. cyg_drv_interrupt_deleteFunction: void cyg_drv_interrupt_delete( cyg_handle_t interrupt )Arguments:interrupt - interrupt to deleteResult: None Level: ThreadDescription: Detach the interrupt from the vector and free the memory
passed in the intr argument to
cyg_drv_interrupt_create() for
reuse. cyg_drv_interrupt_attachFunction: void cyg_drv_interrupt_attach( cyg_handle_t interrupt )Arguments:interrupt - interrupt to attachResult: None Level: ISRDescription: Attach the interrupt to the vector so that interrupts will
be delivered to the ISR when the interrupt occurs. cyg_drv_interrupt_detachFunction: void cyg_drv_interrupt_detach( cyg_handle_t interrupt )Arguments:interrupt - interrupt to detachResult: None Level: ISRDescription: Detach the interrupt from the vector so that interrupts
will no longer be delivered to the ISR. cyg_drv_interrupt_maskFunction: void cyg_drv_interrupt_mask(cyg_vector_t vector )Arguments:vector - vector to maskResult: None Level: ISRDescription: Program the interrupt controller to stop delivery of
interrupts on the given vector. On architectures which implement
interrupt priority levels this may also disable all lower
priority interrupts. cyg_drv_interrupt_mask_intunsafeFunction: void cyg_drv_interrupt_mask_intunsafe(cyg_vector_t vector )Arguments:vector - vector to maskResult: None Level: ISRDescription: Program the interrupt controller to stop delivery of
interrupts on the given vector. On architectures which implement
interrupt priority levels this may also disable all lower
priority interrupts. This version differs from
cyg_drv_interrupt_mask() in not being
interrupt safe. So in situations where, for example, interrupts
are already known to be disabled, this may be called to avoid
the extra overhead.cyg_drv_interrupt_unmaskFunction: void cyg_drv_interrupt_unmask(cyg_vector_t vector )Arguments:vector - vector to unmaskResult: None Level: ISRDescription: Program the interrupt controller to re-allow delivery of
interrupts on the given vector. cyg_drv_interrupt_unmask_intunsafeFunction: void cyg_drv_interrupt_unmask_intunsafe(cyg_vector_t vector )Arguments:vector - vector to unmaskResult: None Level: ISRDescription: Program the interrupt controller to re-allow delivery of
interrupts on the given vector. This
version differs from
cyg_drv_interrupt_unmask() in not being
interrupt safe.cyg_drv_interrupt_acknowledgeFunction: void cyg_drv_interrupt_acknowledge( cyg_vector_t vector )Arguments:vector - vector to acknowledgeResult: None Level: ISRDescription: Perform any processing required at the interrupt
controller and in the CPU to cancel the current interrupt
request on the vector. An ISR may also
need to program the hardware of the device to prevent an
immediate re-triggering of the interrupt. cyg_drv_interrupt_configureFunction:
void cyg_drv_interrupt_configure( cyg_vector_t vector,
cyg_bool_t level,
cyg_bool_t up
)
Arguments:vector - vector to configurelevel - level or edge triggeredup - rising/falling edge, high/low levelResult: NoneLevel: ISRDescription: Program the interrupt controller with the characteristics
of the interrupt source. The level
argument chooses between level- or edge-triggered
interrupts. The up argument chooses
between high and low level for level triggered interrupts or
rising and falling edges for edge triggered interrupts. This
function only works with interrupt controllers that can control
these parameters. cyg_drv_interrupt_levelFunction:
void cyg_drv_interrupt_level( cyg_vector_t vector,
cyg_priority_t level
)
Arguments:vector - vector to configurelevel - level to setResult: None Level: ISRDescription: Program the interrupt controller to deliver the given
interrupt at the supplied priority level. This function only
works with interrupt controllers that can control this
parameter.cyg_drv_interrupt_set_cpuFunction:
void cyg_drv_interrupt_set_cpu( cyg_vector_t vector,
cyg_cpu_t cpu
)
Arguments:vector - interrupt vector to routecpu - destination CPUResult:NoneLevel:ISRDescription:
This function causes all interrupts on the given vector to be
routed to the specified CPU. Subsequently, all such interrupts
will be handled by that CPU. This only works if the underlying
hardware is capable of performing this kind of routing. This
function does nothing on a single CPU system.
cyg_drv_interrupt_get_cpuFunction:
cyg_cpu_t cyg_drv_interrupt_set_cpu( cyg_vector_t vector )
Arguments:vector - interrupt vector to queryResult:The CPU to which this vector is routedLevel:ISRDescription:
In multi-processor systems this function returns the id of the
CPU to which interrupts on the given vector are current being
delivered. In single CPU systems this function returns zero.
cyg_ISR_tType:
typedef cyg_uint32 cyg_ISR_t( cyg_vector_t vector,
cyg_addrword_t data
)
Fields:vector - vector being delivereddata - data value supplied by clientResult: Bit mask indicating whether interrupt was handled and
whether the DSR should be called. Description: Interrupt Service Routine definition. A pointer to a
function with this prototype is passed to
cyg_interrupt_create() when an interrupt
object is created. When an interrupt is delivered the function
will be called with the vector number and the data value that
was passed to cyg_interrupt_create().
The return value is a bit mask containing one or both of the
following bits: CYG_ISR_HANDLED indicates that the interrupt was handled by this
ISR. It is a configuration option whether this will
prevent further ISR being run. CYG_ISR_CALL_DSR causes the DSR that was passed to
cyg_interrupt_create() to be
scheduled to be called.cyg_DSR_tType:
typedef void cyg_DSR_t( cyg_vector_t vector,
cyg_ucount32 count,
cyg_addrword_t data
)
Fields:vector - vector being deliveredcount - number of times DSR has been scheduleddata - data value supplied by clientResult: NoneDescription: Deferred Service Routine prototype. A pointer to a
function with this prototype is passed to
cyg_interrupt_create() when an interrupt
object is created. When the ISR requests the scheduling of its
DSR, this function will be called at some later point. In
addition to the vector and
data arguments, which will be the same as
those passed to the ISR, this routine is also passed a
count of the number of times the ISR has
requested that this DSR be scheduled. This counter is zeroed
each time the DSR actually runs, so it indicates how many
interrupts have occurred since it last ran.