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. <!-- <index></index> -->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).
<!-- <index></index> -->API Details
cyg_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_INFO Buf type: cyg_serial_info_t Function: 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_INFO Buf type: cyg_serial_buf_info_t Function: 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_DRAIN Buf 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_FLUSH Buf type: void * Function: This function discards any buffered output for the device. CYG_IO_GET_CONFIG_SERIAL_INPUT_DRAIN Buf type: void * Function: This function discards any buffered input for the device. CYG_IO_GET_CONFIG_SERIAL_ABORT Buf 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_BLOCKING Buf 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_BLOCKING Buf 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_INFO Buf type: cyg_serial_info_t Function: 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_BLOCKING Buf 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_BLOCKING Buf 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.
<!-- <index></index> -->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_INFO Buf type: cyg_tty_info_t Function: 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_INFO Buf type: cyg_tty_info_t Function: 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) Arguments l The "C" label for this device table entry. name The "C" string name for the device. dep_name For a layered device, the "C" string name of the device this device is built upon. handlers A pointer to the I/O function "handlers" (see below). init A function called when eCos is initialized. This function can query the device, setup hardware, etc. lookup A function called when cyg_io_lookup() is called for this device. priv A 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) Arguments l The "C" label for this table of handlers. write The function called as a result of cyg_io_write(). read The function called as a result of cyg_io_read(). select The function called as a result of cyg_io_select(). get_config The function called as a result of cyg_io_get_config(). set_config The 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 Driver The 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 Entry The 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>> ); Arguments module_name The "C" label for this devtab entry device_name The "C" string for the device. E.g. /dev/serial0. serial_devio The table of I/O functions. This set is defined in the hardware independent serial driver and should be used. module_init The module initialization function. module_lookup The device lookup function. This function typically sets up the device for actual use, turning on interrupts, configuring the port, etc. serial_channel This table (defined below) contains the interface between the interface module and the serial driver proper.
Serial Channel Structure Each 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) Arguments l The "C" label for this structure. funs The set of interface functions (see below). dev_priv A placeholder for any device specific data for this channel. baud The initial baud rate value (cyg_serial_baud_t). stop The initial stop bits value (cyg_serial_stop_bits_t). parity The initial parity mode value (cyg_serial_parity_t). word_length The initial word length value (cyg_serial_word_length_t). flags The initial driver flags value. out_buf Pointer to the output buffer. NULL if none required. out_buflen The length of the output buffer. in_buf pointer to the input buffer. NULL if none required. in_buflen The 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) Arguments l The "C" label for this structure. putc bool (*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 getc unsigned 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_config bool (*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_xmit void (*start_xmit)(serial_channel *priv) In interrupt mode, turn on the transmitter and allow for transmit interrupts. stop_xmit void (*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_filter
Rationale 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 Protocol The protocol commands are prefixed with an "@" character which the serial filter is looking for. The protocol commands include: PING Allows 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. CONFIG Requests 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.] BINARY Requests 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/ serial1 A simple API test. serial2 A simple serial send test. It writes out two strings, one raw and one encoded as a GDB O-packet serial3 [ 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 Usage Running 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/serial3 Then 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.
Debugging If 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 Model eCos 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.
<!-- <index></index> -->Synchronization There 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.
<!-- <index></index> -->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>.
<!-- <index></index> -->cyg_drv_isr_lock Function: void cyg_drv_isr_lock() Arguments: None Result: None Level: ISR Description: Disables delivery of interrupts, preventing all ISRs running. This function maintains a counter of the number of times it is called.
<!-- <index></index> -->cyg_drv_isr_unlock Function: void cyg_drv_isr_unlock() Arguments: None Result: None Level: ISR Description: 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.
<!-- <index></index> -->cyg_drv_spinlock_init Function: void cyg_drv_spinlock_init(cyg_spinlock_t *lock, cyg_bool_t locked ) Arguments: lock - pointer to spinlock to initialize locked - initial state of lock Result: None Level: Thread Description: Initialize a spinlock. The locked argument indicates how the spinlock should be initialized: TRUE for locked or FALSE for unlocked state.
<!-- <index></index> -->cyg_drv_spinlock_destroy Function: void cyg_drv_spinlock_destroy(cyg_spinlock_t *lock ) Arguments: lock - pointer to spinlock destroy Result: None Level: Thread Description: 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.
<!-- <index></index> -->cyg_drv_spinlock_spin Function: void cyg_drv_spinlock_spin(cyg_spinlock_t *lock ) Arguments: lock - pointer to spinlock to claim Result: None Level: ISR Description: 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().
<!-- <index></index> -->cyg_drv_spinlock_clear Function: void cyg_drv_spinlock_clear(cyg_spinlock_t *lock ) Arguments: lock - pointer to spinlock to clear Result: None Level: ISR Description: 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.
<!-- <index></index> -->cyg_drv_spinlock_try Function: cyg_bool_t cyg_drv_spinlock_try(cyg_spinlock_t *lock ) Arguments: lock - pointer to spinlock to try Result: TRUE if the spinlock was claimed, FALSE otherwise. Level: ISR Description: 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.
<!-- <index></index> -->cyg_drv_spinlock_test Function: cyg_bool_t cyg_drv_spinlock_test(cyg_spinlock_t *lock ) Arguments: lock - pointer to spinlock to test Result: TRUE if the spinlock is available, FALSE otherwise. Level: ISR Description: 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.
<!-- <index></index> -->cyg_drv_spinlock_spin_intsave Function: void cyg_drv_spinlock_spin_intsave(cyg_spinlock_t *lock, cyg_addrword_t *istate ) Arguments: lock - pointer to spinlock to claim istate - pointer to interrupt state save location Result: None Level: ISR Description: 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.
<!-- <index></index> -->cyg_drv_spinlock_clear_intsave Function: 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: None Level: ISR Description: 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().
<!-- <index></index> -->cyg_drv_dsr_lock Function: void cyg_drv_dsr_lock() Arguments: None Result: None Level: DSR Description: Disables scheduling of DSRs. This function maintains a counter of the number of times it has been called.
<!-- <index></index> -->cyg_drv_dsr_unlock Function: void cyg_drv_dsr_unlock() Arguments: None Result: None Level: DSR Description: 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.
<!-- <index></index> -->cyg_drv_mutex_init Function: void cyg_drv_mutex_init(cyg_drv_mutex_t *mutex) Arguments: mutex - pointer to mutex to initialize Result: None Level: Thread Description: Initialize the mutex pointed to by the mutex argument.
<!-- <index></index> -->cyg_drv_mutex_destroy Function: void cyg_drv_mutex_destroy( cyg_drv_mutex_t *mutex ) Arguments: mutex - pointer to mutex to destroy Result: None Level: Thread Description: 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.
<!-- <index></index> -->cyg_drv_mutex_lock Function: cyg_bool cyg_drv_mutex_lock( cyg_drv_mutex_t *mutex ) Arguments: mutex - pointer to mutex to lock Result: TRUE it the thread has claimed the lock, FALSE otherwise. Level: Thread Description: 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.
<!-- <index></index> -->cyg_drv_mutex_trylock Function: cyg_bool cyg_drv_mutex_trylock( cyg_drv_mutex_t *mutex ) Arguments: mutex - pointer to mutex to lock Result: TRUE if the mutex has been locked, FALSE otherwise. Level: Thread Description: 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.
<!-- <index></index> -->cyg_drv_mutex_unlock Function: void cyg_drv_mutex_unlock( cyg_drv_mutex_t *mutex ) Arguments: mutex - pointer to mutex to unlock Result: None Level: Thread Description: 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.
<!-- <index></index> -->cyg_drv_mutex_release Function: void cyg_drv_mutex_release( cyg_drv_mutex_t *mutex ) Arguments: mutex - pointer to mutex to release Result: None Level: Thread Description: 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.
<!-- <index></index> -->cyg_drv_cond_init Function: void cyg_drv_cond_init( cyg_drv_cond_t *cond, cyg_drv_mutex_t *mutex ) Arguments: cond - condition variable to initialize mutex - mutex to associate with this condition variable Result: None Level: 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.
<!-- <index></index> -->cyg_drv_cond_destroy Function: void cyg_drv_cond_destroy( cyg_drv_cond_t *cond ) Arguments: cond - condition variable to destroy Result: None Level: Thread Description: Destroy the condition variable pointed to by the cond argument.
<!-- <index></index> -->cyg_drv_cond_wait Function: void cyg_drv_cond_wait( cyg_drv_cond_t *cond ) Arguments: cond - condition variable to wait on Result: None Level: Thread Description: 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.
<!-- <index></index> -->cyg_drv_cond_signal Function: void cyg_drv_cond_signal( cyg_drv_cond_t *cond ) Arguments: cond - condition variable to signal Result: None Level: DSR Description: 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().
<!-- <index></index> -->cyg_drv_cond_broadcast Function: void cyg_drv_cond_broadcast( cyg_drv_cond_t *cond ) Arguments: cond - condition variable to broadcast to Result: None Level: DSR Description: Signal the condition variable pointed to by the cond argument. If there are any threads waiting on this variable they will all be awakened.
<!-- <index></index> -->cyg_drv_interrupt_create Function: 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 to priority - queuing priority data - data pointer isr - interrupt service routine dsr - deferred service routine handle - returned handle intr - put interrupt object here Result: None Level: Thread Description: 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.
<!-- <index></index> -->cyg_drv_interrupt_delete Function: void cyg_drv_interrupt_delete( cyg_handle_t interrupt ) Arguments: interrupt - interrupt to delete Result: None Level: Thread Description: Detach the interrupt from the vector and free the memory passed in the intr argument to cyg_drv_interrupt_create() for reuse.
<!-- <index></index> -->cyg_drv_interrupt_attach Function: void cyg_drv_interrupt_attach( cyg_handle_t interrupt ) Arguments: interrupt - interrupt to attach Result: None Level: ISR Description: Attach the interrupt to the vector so that interrupts will be delivered to the ISR when the interrupt occurs.
<!-- <index></index> -->cyg_drv_interrupt_detach Function: void cyg_drv_interrupt_detach( cyg_handle_t interrupt ) Arguments: interrupt - interrupt to detach Result: None Level: ISR Description: Detach the interrupt from the vector so that interrupts will no longer be delivered to the ISR.
<!-- <index></index> -->cyg_drv_interrupt_mask Function: void cyg_drv_interrupt_mask(cyg_vector_t vector ) Arguments: vector - vector to mask Result: None Level: ISR Description: 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.
<!-- <index></index> -->cyg_drv_interrupt_mask_intunsafe Function: void cyg_drv_interrupt_mask_intunsafe(cyg_vector_t vector ) Arguments: vector - vector to mask Result: None Level: ISR Description: 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.
<!-- <index></index> -->cyg_drv_interrupt_unmask Function: void cyg_drv_interrupt_unmask(cyg_vector_t vector ) Arguments: vector - vector to unmask Result: None Level: ISR Description: Program the interrupt controller to re-allow delivery of interrupts on the given vector.
<!-- <index></index> -->cyg_drv_interrupt_unmask_intunsafe Function: void cyg_drv_interrupt_unmask_intunsafe(cyg_vector_t vector ) Arguments: vector - vector to unmask Result: None Level: ISR Description: 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.
<!-- <index></index> -->cyg_drv_interrupt_acknowledge Function: void cyg_drv_interrupt_acknowledge( cyg_vector_t vector ) Arguments: vector - vector to acknowledge Result: None Level: ISR Description: 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.
<!-- <index></index> -->cyg_drv_interrupt_configure Function: void cyg_drv_interrupt_configure( cyg_vector_t vector, cyg_bool_t level, cyg_bool_t up ) Arguments: vector - vector to configure level - level or edge triggered up - rising/falling edge, high/low level Result: None Level: ISR Description: 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.
<!-- <index></index> -->cyg_drv_interrupt_level Function: void cyg_drv_interrupt_level( cyg_vector_t vector, cyg_priority_t level ) Arguments: vector - vector to configure level - level to set Result: None Level: ISR Description: 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.
<!-- <index></index> -->cyg_drv_interrupt_set_cpu Function: void cyg_drv_interrupt_set_cpu( cyg_vector_t vector, cyg_cpu_t cpu ) Arguments: vector - interrupt vector to route cpu - destination CPU Result: None Level: ISR Description: 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.
<!-- <index></index> -->cyg_drv_interrupt_get_cpu Function: cyg_cpu_t cyg_drv_interrupt_set_cpu( cyg_vector_t vector ) Arguments: vector - interrupt vector to query Result: The CPU to which this vector is routed Level: ISR Description: 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.
<!-- <index></index> -->cyg_ISR_t Type: typedef cyg_uint32 cyg_ISR_t( cyg_vector_t vector, cyg_addrword_t data ) Fields: vector - vector being delivered data - data value supplied by client Result: 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.
<!-- <index></index> -->cyg_DSR_t Type: typedef void cyg_DSR_t( cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data ) Fields: vector - vector being delivered count - number of times DSR has been scheduled data - data value supplied by client Result: None Description: 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.