Adding CAN support for a new device to eCos involves a number of
steps. First a new package for the driver must be created and added to
ecos.db. This package must contain CDL, headers and sources for
implementing the driver. If the driver can apply to devices on
different platforms, then a further package that configures the
generic device to each platform will also be needed.
Device Driver Data Structures
Each CAN device is represented by a
cyg_can_device structure. Most of the fields
of this structure are private to the CAN subsystem, and it needs to be
defined in such a way that it is included in a table of all the CAN
devices. To make this simple for the driver writer a macro has been
defined to create this structure for each channel.
CYG_CAN_DEVICE( tag, chan, name, priv );
The arguments to this macro are:
tag
This is a general name for the device driver as a whole, it should
usually be the name of the device chip or interface type. Typical
values might be sja1000 or
flexcan. This tag is used to ensure that the data
structures declared by this macro are unique to this driver.
chan
This distinguishes between separate channels supported
by the driver. Typical values might be can0 or
can1.
name
This is the name of the channel as used in the
cyg_can_open() function. It is a string constant
and is usually defined by the driver's CDL. Typical CAN channel names
are "can0" and "can1". However this argument will usually be a CDL
option such as CYGPKG_DEVS_CAN_CHANNEL0_NAME or
CYGPKG_DEVS_CAN_CHANNEL1_NAME.
priv
This is a pointer to a private data structure that contains device
specific information such as its base address and interrupt
vector. The CAN subsystem does not interpret the contents of this in
any way. Typical values for this might be
cyg_can_flexcan_drv_0 or
cyg_can_sja1000_drv[1].
The interface to each driver is via a table of function calls that is
pointed to by the cyg_can_device
structure. This structure has the following definition:
struct cyg_can_device_calls {
int (*init) (cyg_can_device *dev);
int (*open) (cyg_can_device *dev);
int (*close) (cyg_can_device *dev);
int (*send) (cyg_can_device *dev, cyg_can_msg *msg);
int (*poll) (cyg_can_device *dev);
int (*filter)(cyg_can_device *dev, cyg_bool ide, cyg_uint32 match, cyg_uint32 mask);
int (*baud) (cyg_can_device *dev, cyg_uint32 baud);
};
init()
This is called to initialize the channel when
cyg_can_init() is called. It should locate the
channel and initialize it ready for communication. It should also
install any interrupts and initialize the fields of the private data
structure.
open()
This is called if the name of this channel matches the device name
passed to cyg_can_open(). There is no requirement
for the driver to do anything here, but possible things it might do is
to allocate per-client resources in the hardware, or just keep count
of the number of users.
close()
This is called when cyg_can_close() is called. As
with the open() function, there is no required
behaviour here, but if open() allocated
resources, then this is where they should be released.
send()
This function is called to transmit a message by
cyg_can_send() and is passed a pointer to a
message buffer. This function should return one of several return
codes depending on the state of the channel:
CYG_CAN_BUSY
The channel is busy and cannot transmit the message at this point. The
CAN subsystem will add the message to its pending queue until the
channel is available, at which point the buffer will be passed back to
the driver by the cyg_can_tx_done() function.
CYG_CAN_WAIT
The channel has started the transmission of the message, but it is
not yet complete. The CAN subsystem will cause the sending thread to
wait until the driver calls
cyg_can_tx_done(). This is usually used by
interrupt driven drivers to make the sender wait for the transmit
completion interrupt.
CYG_CAN_NOERROR
The channel has transmitted the message immediately, and has also
called cyg_can_tx_done() to complete the
process. This is mainly used by polled drivers, which don't use
interrupts.
CYG_CAN_*
Any other error code indicates a hardware error of some sort and will
be passed back to the caller by the CAN subsystem.
poll()
This is usually called from the driver's DSR routine to handle any
events that have occurred on the channel. It may also be called from
the CAN subsystem at various times, and may be called from application
code calling cyg_can_poll().
filter()
This function is called to set the hardware ID filter. Not all
hardware supports a filter mechanism that matches the model
implemented by the CAN subsystem. This function should return
CYG_CAN_NOERROR whether it can set the hardware
filter or not. The CAN subsystem will always apply the filter in
software to all received messages, so setting the hardware filter is
an optimization to reduce the number of messages received. There is no
function to return the filter since the CAN subsystem records the
filter itself.
baud()
This function is called to set the network baud rate. The driver is at
liberty to support only a subset of the possible baud rates, and
return CYG_CAN_INVALID for those it cannot. It is
also possible for certain baud rates not be supported for certain
system clock speeds due to limitations in the clock divider. It may
not be possible for the driver to detect this.
The CAN subsystem defines a macro to create this function table:
CYG_CAN_DEVICE_CALLS( tag );
The tag argument should match the
tag argument of the
CYG_CAN_DEVICE() macro. This macro does two things.
First it declares static function prototypes for all the driver
functions of the form
cyg_can_<tag>_<function>
(e.g. cyg_can_sja1000_init() or
cyg_can_flexcan_send()). Second, it defines a
function table called
cyg_can_<tag>_calls. The
CYG_CAN_DEVICE() macro assumes that a function
table of this name is defined.
Device Driver API Calls
In addition to the standard device driver API calls defined by the
kernel, there are a number of additional CAN specific API calls that a
device driver must use to interact with the CAN subsystem. These
functions may only be called from thread level with the DSR lock
claimed, or from a DSR. They cannot be called from an ISR.
This must be called when a transmission is completed. For polled
drivers it should be called from the send()
function while for interrupt driven drivers it should be called from
the poll() routine invoked from the driver's
DSR. The function is called with a pointer to the transmitted message
buffer and following this call the message buffer will become the
property of the CAN subsystem and may be returned to the free
pool. The return value from this function will a pointer be another
message buffer to transmit, or NULL. The driver should use the
resources recently freed by the completion of the previous
transmission to start transmission of this message.
cyg_can_msg *cyg_can_rx_buffer( void );
This is called by the driver to acquire a message buffer in to which a
message will be received. The normal approach is to allocate a buffer
during driver initialization and to keep at least one pending buffer
available at all times. New buffers will usually be passed back to the
running driver by the cyg_can_rx_done() function.
This must be called when a driver has a completed message buffer to
return to the user. This buffer may either contain a received message,
or may be reporting a channel event. This call is made with a
pointer to the message buffer to be returned. Following this call the
message buffer becomes the property of the CAN subsystem. The return
value of this function will be a pointer to a message buffer to
replace the one just passed back. Thus with one call the driver both
gives up an old buffer and gets a new one to use in its place.
It is possible for cyg_can_rx_done() to return a
NULL pointer if there are currently no more buffers available. The
driver must therefore be able to handle this. The usual approach is to
check, just before it is needed, for a current pending buffer. If no
buffer is present then call cyg_can_rx_buffer()
and if this returns NULL then take action to, for example, throw the
message away.
Configuration
The only direct configuration requirement on device drivers is that
for each channel supported, the driver should have an "implements
CYGINT_IO_CAN_DRIVER" statement to ensure that the correct number of
message buffers are available. The name of the channels should also be
defined in the CDL. A minimal CDL file for the XYZZY driver would be
as follows:
cdl_package CYGPKG_DEVS_CAN_XYZZY {
display "XYZZY CAN driver"
description "XYZZY CAN driver."
parent CYGPKG_IO_CAN
active_if CYGPKG_IO_CAN
include_dir cyg/devs/can
compile -library=libextras.a xyzzy.c
cdl_component CYGPKG_DEVS_CAN_CHANNEL0 {
display "CAN channel 0 configuration"
default_value 1
implements CYGINT_IO_CAN_DRIVER
cdl_option CYGPKG_DEVS_CAN_CHANNEL0_NAME {
display "CAN channel 0 name"
flavor data
default_value { "\"can0\"" }
description "Name of CAN channel 0"
}
}
cdl_component CYGPKG_DEVS_CAN_CHANNEL1 {
display "CAN channel 1 configuration"
default_value 1
implements CYGINT_IO_CAN_DRIVER
cdl_option CYGPKG_DEVS_CAN_CHANNEL1_NAME {
display "CAN channel 1 name"
flavor data
default_value { "\"can1\"" }
description "Name of CAN channel 1"
}
}
# Further entries for extra channels would go here
}
If the driver is multi-platform, then the channel configurations
should go into the second platform specific package which may also
need to define suitable configuration options to customize the generic
driver.
Driver Template
The following example show the general structure of a CAN device
driver for a fictional XYZZY device.
The first thing we need to do is
to define the data structures that interface the device to the CAN
subsystem:
#include <pkgconf/hal.h>
#include <pkgconf/io_can.h>
#include <pkgconf/devs_can_xyzzy.h>
#include <cyg/io/can_dev.h>
//=============================================================================
// Define private data structure. At the very least this needs to
// contain the base address of the device and the interrupt vector.
// If this is an interrupt driven device, then it will also need to
// contain the data structures to manage the interrupt.
struct cyg_can_xyzzy_priv
{
cyg_uint32 devno; // device number
CYG_ADDRESS base; // base address
cyg_vector_t vector; // vector number
cyg_can_msg *tx_msg; // current tx message buffer
cyg_can_msg *rx_msg; // pending rx message buffer
cyg_handle_t interrupt_handle;
cyg_interrupt interrupt_object;
// Further device fields here
};
//=============================================================================
// Define device function call table. This should be done before the
// CYG_CAN_DEVICE() macro is called.
CYG_CAN_DEVICE_CALLS( xyzzy );
//=============================================================================
// Define driver-private structures for each of the channels. For this
// example we define just two.
#ifdef CYGPKG_DEVS_CAN_CHANNEL0
struct cyg_can_xyzzy_priv cyg_can_xyzzy_drv_0 =
{ 0, CYGARC_HAL_XYZZY_BASE_CAN_0, CYGNUM_HAL_INTERRUPT_CAN_0 };
#endif
#ifdef CYGPKG_DEVS_CAN_CHANNEL1
struct cyg_can_xyzzy_priv cyg_can_xyzzy_drv_1 =
{ 1, CYGARC_HAL_XYZZY_BASE_CAN_1, CYGNUM_HAL_INTERRUPT_CAN_1 };
#endif
//=============================================================================
// Define CAN device table entries.
#ifdef CYGPKG_DEVS_CAN_CHANNEL0
CYG_CAN_DEVICE( xyzzy, can0, CYGPKG_DEVS_CAN_CHANNEL0_NAME, cyg_can_xyzzy_drv_0 );
#endif
#ifdef CYGPKG_DEVS_CAN_CHANNEL1
CYG_CAN_DEVICE( xyzzy, can1, CYGPKG_DEVS_CAN_CHANNEL1_NAME, cyg_can_xyzzy_drv_1 );
#endif
The first thing that needs writing is the initialization routine:
static int cyg_can_xyzzy_init(cyg_can_device *dev)
{
int result = CYG_CAN_NOERROR;
struct cyg_can_xyzzy_priv *priv = (struct cyg_can_xyzzy_priv *)dev->private;
// Locate, validate and initialize the channel hardware. This
// may include setting up the acceptance filter to accept all IDs
// and setting the baud rate to a default (100kHz say).
// Install interrupt handlers
cyg_drv_interrupt_create( priv->vector,
0,
(CYG_ADDRWORD)dev,
cyg_can_xyzzy_isr,
cyg_can_xyzzy_dsr,
&priv->interrupt_handle,
&priv->interrupt_object );
cyg_drv_interrupt_attach( priv->interrupt_handle );
cyg_drv_interrupt_unmask( priv->vector );
// Perform any final initialization, for example clearing and then
// enabling interrupts in the channel.
// Allocate a pending buffer for message receive.
priv->rx_msg = cyg_can_rx_buffer();
return result;
}
The open and close routines come next. Most drivers don't need to do
much here so these examples are the minimum necessary:
static int cyg_can_xyzzy_open(cyg_can_device *dev)
{
return CYG_CAN_NOERROR;
}
static int cyg_can_xyzzy_close(cyg_can_device *dev)
{
return CYG_CAN_NOERROR;
}
The send routine is responsible for actually transmitting a message:
static int cyg_can_xyzzy_send(cyg_can_device *dev, cyg_can_msg *msg)
{
struct cyg_can_xyzzy_priv *priv = (struct cyg_can_xyzzy_priv *)dev->private;
// If there is still a current tx message or the transmit hardware
// is still busy, return busy so that the upper levels will
// queue this request.
if( priv->tx_msg != NULL || xyzzy_tx_busy( priv ) )
return CYG_CAN_BUSY;
// Record current transmit packet
priv->tx_msg = msg;
// Write the message header and ID to be sent into the channel
// transmit buffer, ensuring that the length and the IDE bit is
// set correctly, and the ID is correct.
// If the RTR flag is not set, install the data in the transmit
// buffer. If the RTR flag is set, do not install the data and set
// the RTR bit in the frame info.
// Start the transmission.
// Return CYG_CAN_WAIT to cause the sending thread to wait for completion.
return CYG_CAN_WAIT;
}
The following routine is internal to the driver, it is called from the
poll() routine to actually receive a message into
a buffer:
static int cyg_can_xyzzy_recv(cyg_can_device *dev, cyg_can_msg *msg)
{
int result = CYG_CAN_NOERROR;
struct cyg_can_xyzzy_priv *priv = (struct cyg_can_xyzzy_priv *)dev->private;
cyg_ucount8 ide, rtr, len;
cyg_uint32 id = 0;
// Get the message frame header and decode it into some locals:
// ide, rtr, len and id.
// If we have a message buffer, move the message out into it.
if( msg != NULL )
{
msg->ide = ide;
msg->rtr = rtr;
msg->len = len;
msg->id = id;
if( !rtr )
{
// Copy data from receive frame into message buffer.
}
}
else
{
// Do whatever is needed to throw the message away since
// there is no buffer available.
}
// Do whatever is needed to release the receive buffer and ready
// it for a new message and/or cancel the interrupt.
return result;
}
The poll() handles most of the asynchronous events:
static int cyg_can_xyzzy_poll(cyg_can_device *dev)
{
int result = CYG_CAN_NOERROR;
struct cyg_can_xyzzy_priv *priv = (struct cyg_can_xyzzy_priv *)dev->private;
// If there is a pending transmission and the hardware channel
// indicates that it is finished, then call cyg_can_tx_done().
if( priv->tx_msg != NULL && xyzzy_tx_done( priv ) )
{
cyg_can_msg *msg = priv->tx_msg;
priv->tx_msg = NULL;
msg = cyg_can_tx_done( dev, msg );
// If we have been passed a new message to transmit, send it.
if( msg != NULL )
cyg_can_xyzzy_send( dev, msg );
}
// While there are messages available in the receive buffer or
// FIFO, pull them out and pass them back to the CAN subsystem.
while( xyzzy_rx_done( priv ) )
{
// If there is no current rx buffer, try to allocate one here.
if( priv->rx_msg == NULL )
priv->rx_msg = cyg_can_rx_buffer();
// Either receive the message, or clear the channel.
cyg_can_xyzzy_recv( dev, priv->rx_msg );
// If we have a buffer, pass it back.
if( priv->rx_msg != NULL )
priv->rx_msg = cyg_can_rx_done( dev, priv->rx_msg );
}
// See if any other CAN channel events have occurred.
if( xyzzy_event( priv ) )
{
// Decode the event and set result to an appropriate error
// code.
// Return a message buffer recording this event. As above, we
// may need to allocate a fresh buffer if none is available.
if( result != CYG_CAN_NOERROR )
{
if( priv->rx_msg == NULL )
priv->rx_msg = cyg_can_rx_buffer();
if( priv->rx_msg != NULL )
{
priv->rx_msg->result = result;
priv->rx_msg = cyg_can_rx_done( dev, priv->rx_msg );
}
result = CYG_CAN_NOERROR;
}
}
return result;
}
The simplest form for the ISR is for it to just mask the channel's
interrupt vector and cause the DSR to run. The DSR can then simply
call cyg_can_xyzzy_poll() to handle the channel
events. Alternatively, the ISR could handle the hardware, but the DSR
still needs to be run to call cyg_can_tx_done(),
cyg_can_rx_done() and
cyg_can_rx_buffer().
static cyg_uint32 cyg_can_xyzzy_isr(cyg_vector_t vector, cyg_addrword_t data)
{
cyg_can_device *dev = (cyg_can_device *)data;
// Block interrupts from this device until the DSR is run
cyg_drv_interrupt_mask( vector );
// Ack the interrupt in the system interrupt controller
cyg_drv_interrupt_acknowledge( vector );
// Pass handling on to DSR
return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR);
}
static void cyg_can_xyzzy_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
cyg_can_device *dev = (cyg_can_device *)data;
// Poll hardware for pending events
cyg_can_xyzzy_poll( dev );
// Re-allow device interrupts
cyg_drv_interrupt_unmask( vector );
}
Finally, the filter and baud rate functions are very simple:
static int cyg_can_xyzzy_filter(cyg_can_device *dev, cyg_bool ide, cyg_uint32 match, cyg_uint32 mask)
{
int result = CYG_CAN_NOERROR;
struct cyg_can_xyzzy_priv *priv = (struct cyg_can_xyzzy_priv *)dev->private;
// Set the hardware filter to match the parameters. If the
// hardware filter cannot be used, return CYG_CAN_NOERROR anyway,
// since filtering will also be done in the CAN subsystem.
return result;
}
static int cyg_can_xyzzy_baud(cyg_can_device *dev, cyg_uint32 baud)
{
int result = CYG_CAN_NOERROR;
struct cyg_can_xyzzy_priv *priv = (struct cyg_can_xyzzy_priv *)dev->private;
// Set the baud rate, which may involve checking that the
// requested rate is supported. If not the return
// CYG_CAN_INVALID.
result = cyg_can_xyzzy_set_baudrate( priv, baud );
return result;
}