1. CAN Driver¶
RTEMS provides fully featured CAN/CAN FD stack. The API to the driver is provided in the form of the POSIX character driver with each CAN controller (chip) registered as node into “/dev” namespace by name (i.e. “can0”, “can1”, … ). The stack utilizes FIFO queues (also called edges) organized into oriented edges between controller side and application side. Edges, responsible for message transfers from application to controller and vice versa, can have different priorities and function as priority classes.
Controller takes frames from FIFOs according their priority class and transmits them to the network. Successfully sent frames are echoed through queues back to open file instances except the sending one (that filtering is configurable). Received frames a filtered to all queues to applications ends of the queues which filter matches the CAN identifier and frame type.
The stack provides run time configuration options to create new queues with desired priority, direction and filter, making it suitable for various applications requirements. There is also a possibility to configure controller’s characteristics (bit rate, mode, chip specific ioctl calls).
The device can be opened in both blocking and nonblocking mode and one device can be opened from multiple applications. Read and write operations wait on binary semaphore indefinitely if blocking mode is used and frame can not be passed to the framework immediately (full FIFO queue for example) or there is no received message to process.
1.1. Include Headers¶
To use the infrastructure, an application, BSP initiator or controller device driver has to include few header files that provides related structures, definitions and function declarations.
1.1.1. Application¶
#include <dev/can/can.h>
The only required include for standard application using the structure
through POSIX character device API is <dev/can/can.h>
. This
provides all defines, ioctl calls, and structures required to operate with
the infrastructure from application layer. These are in detail described
in RTEMS CAN API section.
The mentioned header also includes several other headers. These are still
strictly API interface, but separated to multiple headers for clearer
code organization. This contains the header defining CAN frame structure,
header introducing CAN bit timing structures, filters for FIFO queues
or CAN RX/TX statistics tracking. The application does not have to bother
include all these headers as they are already included through <dev/can/can.h>
.
1.1.2. BSP Registration¶
#include <dev/can/can-bus.h>
#include <dev/can/controller-dependent.h>
It is expected the controller will be initialized and registered from board
support package during board initialization, but these includes will work
anywhere else (even from application if required by the user). The header
<dev/can/can-bus.h>
provides definition of can_bus
structure and declarations of can_bus_register()
that registers
the controller to standard /dev namespace. The usage of these functions
is described in Registering CAN Bus section.
The source code will most likely have to also include controller dependent header that declares the initialization function for the specific controller.
1.1.3. Device Driver¶
#include <dev/can/can.h>
#include <dev/can/can-devcommon.h>
The device driver (i.e. the file that implements the controller) has to
include <dev/can/can.h>
because of CAN frame definition and other
defines/structures with which it has to interact. Functions and structures
providing the interface with the infrastructure (obtaining frames from FIFO,
pushing frames to FIFO, slot abort and so on) are included through
<dev/can/can-devcommon.h>
header. The usage of these functions
is described in Driver Interface section.
It is expected <dev/can/can-devcommon.h>
will be used primarily
from a controller device driver (i.e. from RTEMS kernel code), but there
is a possibility to include this header even from an application if the
user has a special needs surpassing the API.
1.2. RTEMS CAN API¶
#include <dev/can/can.h>
It is necessary to include the header above to operate with CAN infrastructure through described application interface.
Application Interface is provided with standard POSIX calls open()
,
write()
, read()
. close()
and ioctl()
.
Functions write()
and read()
are used with
can_frame
structure representing one CAN frame (message).
1.2.1. Opening Device and Configuration¶
Device is registered as a node into “/dev” namespace and can be opened with
POSIX open()
call. A single chip can be opened multiple times
with each instance creating its own queues between controller and application.
- CALLING SEQUENCE:
int open( const char* pathname, int flags );
- DESCRIPTION:
Opens CAN device at path “pathname” with mode defined in “flags”. Modes are defined according to POSIX standard
Both infrastructure resources and controller itself can be configured once the device is opened. The configuration is provided via ioctl calls.
1.2.1.1. Managing Queues¶
One RX and one TX queue is created by default during open()
operation. These queues have the lowest priority and default filter and
size settings. If needed, more queues can be created with
RTEMS_CAN_CREATE_QUEUE
call.
- CALLING SEQUENCE:
ssize_t ioctl( fd, RTEMS_CAN_CREATE_QUEUE, &queue );
- DESCRIPTION:
Creates new queue with characteristics defined in “queue” field provided as
can_queue_param
structure.struct can_queue_param { uint8_t direction; uint8_t priority; uint8_t dlen_max; uint8_t buffer_size; struct can_filter filter; };
Field
dlen_max
set maximum CAN frame data length that can be sent through the queue. This allows the user to limit the size of allocated memory if only shorter frames are sent to the network. If set to zero, default value (64 bytes for CAN FD capable controllers, 8 bytes otherwise) is used. It is not possible to set invalid value (less than zero or greater than 64). Fieldbuffer_size
configures number of slots (frames) that fits in the FIFO.struct can_filter { uint32_t id; uint32_t id_mask; uint32_t flags; uint32_t flags_mask; };
Structure
can_filter
is used to set queue’s filter. It holds the CAN frame identifier and flag filters, ensuring only frames matching this filter are passed to the queue’s ends. Fieldsid
andflags
holds identifier bits and flags, respectively, required in CAN frame in order to assign it to the corresponding FIFO queue. In other words, it specifies that only specific identifiers or flags shall be assigned to the queue. Members with_mask
postfix are used to mask out identifiers or flags that are forbidden for a given FIFO queue. Refer to CAN frame description for possible flags.The filter can be used to create queues that process only defined subset of CAN frames. This may be used to create priority classes based on frame identifier range or special queues for certain type of frames (echo, error etc.). Setting all fields of
can_filter
to zero means all frames are passed through the queue.Default queues created during
open()
operation allows all identifiers and filters out error and echo frames.
Queues can be removed (discarded) with RTEMS_CAN_DISCARD_QUEUES
command.
It is not possible to discard one specific queue, just all RX or/and all TX
queues for given opened instance (file descriptor) at once. Direction can be
defined by RTEMS_CAN_QUEUE_TX
and RTEMS_CAN_QUEUE_RX
defines.
Terms TX and RX are used from application’s point of view: TX meaning queues
transferring messages from application to controller, RX from controller to
application.
- CALLING SEQUENCE:
ssize_t ioctl( fd, RTEMS_CAN_DISCARD_QUEUES, type );
- DESCRIPTION:
Discard TX and/or RX queues based on integer “type” argument. Defines
RTEMS_CAN_QUEUE_TX
andRTEMS_CAN_QUEUE_RX
can be used to specify queues for deletion.
Queues can also be flushed with RTEMS_CAN_FLUSH_QUEUES
command.
- CALLING SEQUENCE:
ssize_t ioctl( fd, RTEMS_CAN_FLUSH_QUEUES, type );
- DESCRIPTION:
Flushes TX and/or RX queues based on integer “type” argument. Defines
RTEMS_CAN_QUEUE_TX
andRTEMS_CAN_QUEUE_RX
can be used to specify queues for deletion. The operation flushes all RX or/and all TX queues even if multiple queues are used.
1.2.1.2. Setting Bit Timing¶
There are two ways to set CAN bit timing. Either the user can pass desired
bit rate value and let the infrastructure calculate bit timing, or precomputed
bit timing values can be passed directly. IOCTL call RTEMS_CAN_SET_BITRATE
is used for this purpose.
- CALLING SEQUENCE:
ssize_t ioctl( fd, RTEMS_CAN_SET_BITRATE, &set_bittiming );
- DESCRIPTION:
Sets bit timing based on “set_bittiming” parameter passed as a pointer to
can_set_bittiming
structure.struct can_bittiming { uint32_t bitrate; uint32_t sample_point; uint32_t tq; uint32_t prop_seg; uint32_t phase_seg1; uint32_t phase_seg2; uint32_t sjw; uint32_t brp; }; struct can_set_bittiming { uint16_t type; uint16_t from; struct can_bittiming bittiming; };
Field “type” determines bit timing to be set (
CAN_BITTIME_TYPE_NOMINAL
orCAN_BITTIME_TYPE_DATA
), field “from” determines source of bit timing values (CAN_BITTIME_FROM_BITRATE
orCAN_BITTIME_FROM_PRECOMPUTED
).
Actual bit timing values and controller’s bit timing constants can
be retrieved with RTEMS_CAN_GET_BITTIMING
.
- CALLING SEQUENCE:
ssize_t ioctl( fd, RTEMS_CAN_GET_BITTIMING, &get_bittiming );
- DESCRIPTION:
Retrieves currently set bit timing values and controller’s bit timing constants.
struct can_bittiming_const { char name[32]; uint32_t tseg1_min; uint32_t tseg1_max; uint32_t tseg2_min; uint32_t tseg2_max; uint32_t sjw_max; uint32_t brp_min; uint32_t brp_max; uint32_t brp_inc; }; struct can_get_bittiming { uint16_t type; struct can_bittiming bittiming; struct can_bittiming_const bittiming_const; };
Field “type” determines bit timing to be set (
CAN_BITTIME_TYPE_NOMINAL
orCAN_BITTIME_TYPE_DATA
).
1.2.1.3. Setting Mode¶
Different mode of the chip can be enabled/disabled. IOCTL call
RTEMS_CAN_CHIP_SET_MODE
is used to set mode as 32-bit large
unsigned integer mask.
- CALLING SEQUENCE:
ssize_t ioctl( fd, RTEMS_CAN_CHIP_SET_MODE, mode );
- DESCRIPTION:
Argument “mode” is a 32-bit large unsigned integer with modes to be set. Available modes are
CAN_CTRLMODE_LOOPBACK
,CAN_CTRLMODE_LISTENONLY
,CAN_CTRLMODE_3_SAMPLES
,CAN_CTRLMODE_ONE_SHOT
,CAN_CTRLMODE_BERR_REPORTING
,CAN_CTRLMODE_FD
,CAN_CTRLMODE_PRESUME_ACK
,CAN_CTRLMODE_FD_NON_ISO
,CAN_CTRLMODE_CC_LEN8_DLC
,CAN_CTRLMODE_TDC_AUTO
, andCAN_CTRLMODE_TDC_MANUAL
.
The modes are selected to be compatible with GNU/Linux’s SocketCAN. It is possible to set multiple modes during one IOCTL call. The controller should be implemented in such a way that not setting particular mode in this IOCTL call disables this mode.
Every controller holds its supported mode. An attempt to set a mode not supported by the controller leads to ioctl call returning error. It is also possible to set mode only if the controller is stopped, otherwise error is returned.
1.2.1.4. Starting Chip¶
Opening the device does not start the chip, this operation has to be handled
by specific ioctl call RTEMS_CAN_CHIP_START
.
- CALLING SEQUENCE:
ssize_t ioctl( fd, RTEMS_CAN_CHIP_START );
- DESCRIPTION:
Starts the chip (enables write/read). Repeated calls on already started chip do not have any effect.
1.2.1.5. Stopping Chip¶
Similarly to chip start operation, chip stop is performed with
RTEMS_CAN_CHIP_STOP
ioctl call.
- CALLING SEQUENCE:
ssize_t ioctl( fd, RTEMS_CAN_CHIP_STOP );
- DESCRIPTION:
Stops the chip (disables write/read). Repeated calls on already stopped chip do not have any effect.
- NOTES.
It is important to check the number of users (applications) using the chip before turning it off as there can be more than one user per chip. The infrastructure allows turning off the controller even if there are other users using it. Read and write calls from other applications return error in that case.
1.2.1.7. Controller Statistics¶
The controller can keep track of its statistics as number of received/transmitted
frames, number of received/transmitted bytes, number of errors and so on. These
statistics are represented in can_stats
structure and can be
obtained with RTEMS_CAN_CHIP_STATISTICS
ioctl call.
- CALLING SEQUENCE:
ssize_t ioctl( fd, RTEMS_CAN_CHIP_STATISTICS, &statistics );
- DESCRIPTION:
Obtains controller’s statistics provided in with argument
statistics
as a pointer tocan_stats
structure.
1.2.2. CAN Frame Representation¶
CAN frame header is statically defined by a separate can_frame_header
.
It has an 8 bytes long timestamp, 4 bytes long CAN identifier, 2 bytes long
flag field and 2 bytes long field with information about data length. Data
field itself is a 64 byte long array with byte access.
Only first 11 bits of the identifier are valid (29 if extended identifier format is used). Having any of upper three bits set to one indicates invalid CAN frame format. The user should check frame’s flags to get information if this is not an error frame generated by the controller.
struct can_frame_header {
uint64_t timestamp;
uint32_t can_id;
uint16_t flags;
uint16_t len;
};
struct can_frame {
struct can_frame_header header;
uint8_t data[CAN_FRAME_MAX_DLEN];
};
Flags are used to distinguish frame formats (extended identifier, CAN FD format, remote request and so on). Following defines can be used.
CAN_FRAME_IDE
,CAN_FRAME_RTR
,CAN_FRAME_ECHO
,CAN_FRAME_LOCAL
,CAN_FRAME_TXERR
,CAN_FRAME_ERR
.CAN_FRAME_FIFO_OVERFLOW
.CAN_FRAME_FDF
,CAN_FRAME_BRS
, andCAN_FRAME_ESI
.
Extended frame format (CAN_FRAME_IDE
) is forced automatically if
identifier exceeds 11 bits. Flags CAN_FRAME_FDF
and
CAN_FRAME_BRS
(if bit rate switch between arbitration and data phase
is intended) should be set for CAN FD frame transmission.
Some of these flags are automatically masked for the first queues created
during instance open operation. These include
CAN_FRAME_ECHO
and both error flags CAN_FRAME_TXERR
and CAN_FRAME_ERR
. Flag CAN_FRAME_FIFO_OVERFLOW
is set automatically by the stack for RX frames and can not be filtered out. It
indicates FIFO overflow occurred, and some frames on receive side have been
discarded. More specifically, it informs the user there are discarded frames
between the frame with CAN_FRAME_FIFO_OVERFLOW
flag and
previous correctly received frame.
1.2.3. Frame Transmission¶
Frame is transmitted to the CAN framework by calling write()
function.
- CALLING SEQUENCE:
ssize_t write( int fd, struct can_frame *frame, size_t count );
- DESCRIPTION:
Passes CAN frame represented by
can_frame
structure to the network. Return values comply with POSIX standard. Write size can be calculated bycan_framesize()
function. It is possible to write just one frame with a single call. Passing incorrect frame length (less than header size or larger than maximum CAN frame size) results in write error.
User can check whether the messages were transferred from RTEMS framework
to CAN network by calling ioctl RTEMS_CAN_WAIT_TX_DONE
.
- CALLING SEQUENCE:
ssize_t ioctl( fd, RTEMS_CAN_WAIT_TX_DONE, &timeout );
- DESCRIPTION:
Waits with timeout until all frames are transferred to the network. The timeout is defined as a pointer to
timespec
structure. Returns 0 on success and “-ETIME” on timeout.
Polling in nonblocking mode can be done with RTEMS_CAN_POLL_TX_READY
ioctl call.
- CALLING SEQUENCE:
ssize_t ioctl( fd, RTEMS_CAN_POLL_TX_READY, &timeout );
- DESCRIPTION:
Implements polling function on outgoing edges. Timeout is defined with
timespec
structure. It waits until there is an available frame in any of the input FIFOs or until timeout.
1.2.4. Frame Reception¶
Frame is received from the CAN framework by calling read()
function.
- CALLING SEQUENCE:
ssize_t read( int fd, struct can_frame *frame, size_t count );
- DESCRIPTION:
Reads CAN frame represented by
can_frame
from the network. Return values comply with POSIX standard. The call returns error if read size specified bycount
is less than the length of the frame header. It is possible to read only a single frame with a read call.
Polling in nonblocking mode can be done with RTEMS_CAN_POLL_RX_AVAIL
ioctl call.
- CALLING SEQUENCE:
ssize_t ioctl( fd, RTEMS_CAN_POLL_RX_AVAIL, &timeout );
- DESCRIPTION:
Implements polling function on incoming edges. Timeout is defined with
timespec
structure. It waits until there is an available frame in any of the input FIFOs or until timeout.
1.2.5. Error Reporting¶
There are two flags for error reporting: CAN_FRAME_TXERR
and CAN_FRAME_ERR
. First flag is used to report frame transmission
error. In this case, the controller should send the frame that caused the
error back to its opened instance with added CAN_FRAME_TXERR
flag.
The message should not be changed in any other way.
It is possible to receive various CAN bus related error through error messages
sent from the controller to the application. Flag CAN_FRAME_ERR
is used for that. If this flag is set, received frame has a special format
and shall be looked up as an error frame.
For generated error frame, identifier field is used to store the information about error type. Following types are supported.
CAN_ERR_ID_TXTIMEOUT
,CAN_ERR_ID_LOSTARB
,CAN_ERR_ID_CRTL
,CAN_ERR_ID_PROT
,CAN_ERR_ID_TRX
,CAN_ERR_ID_ACK
.CAN_ERR_ID_BUSOFF
.CAN_ERR_ID_BUSERROR
,CAN_ERR_ID_RESTARTED
,CAN_ERR_ID_CNT
, andCAN_ERR_ID_INTERNAL
.
Additionally, 31st bit of CAN identifier is set to logical one. This is another check that indicates it is not a regular frame but error one. Having error types located in CAN frame identifier brings the possibility to create new RX queues with identifier mask set in such way that only some of these errors are propagated to the application.
Additional information providing deeper description of raised error are also available in data fields for some error types. Only standard frame with 8 bytes long data field is used.
First byte (8 bits) of data field keeps detailed information regarding
lost arbitration error (CAN_ERR_ID_LOSTARB
). This basically just informs
in that bit the arbitration was lost. Another field stores controller
related problems (CAN_ERR_ID_CRTL
). This includes RX or TX overflows and
controller changing its error state (error active, warning, passive).
Protocol related violations (CAN_ERR_ID_PROT
) are stored in third and fourth
data field. Third field informs what kind of violation is present. This may be
incorrect bit stuffing, controller incapability to generate dominant or
recessive bit or bus overload for example. Fourth field provides location of
this violation.
Transceiver status (CAN_ERR_ID_TRX
) is located in fifth data field.
This is used to report hardware layer issues as missing wire or wire being
short-circuited to ground or supply voltage. Sixth data field is reserved and
not used. The infrastructure also reports number of the current values of TX
and RX error counter (CAN_ERR_ID_CNT
). These data are passed through
seventh and eight data fields for transmission and reception, respectively.
1.3. Driver Interface¶
#include <dev/can/can.h>
#include <dev/can/can-devcommon.h>
Includes mentioned above are required to use the functions described in this section.
The infrastructure provides several functions providing interface between FIFO queue side of the stack and controller specific implementation. Controller’s driver should use these functions to access the FIFO. The driver should include
For examples of CAN controller’s driver see:
1.3.1. Chip Initialization¶
Chip initialization function should allocate can_chip
structure. This
structure holds controller’s ends of FIFO edges as well as chip private
structure. This structure is chip specific and is used to store chip specific
data. Chip’s ends are initialized with canqueue_ends_init_chip()
.
- CALLING SEQUENCE:
int canqueue_ends_init_chip ( struct canque_ends_dev_t *qends_dev, struct can_chip *chip, rtems_binary_semaphore worker_sem );
- DESCRIPTION:
Initializes controller’s side ends defined in
qends_dev
and connects them to the edges. Also assigns worker binary semaphore “worker_sem” used by the framework to inform controller’s side about new message. Controller can also use this semaphore to trigger its worker thread in case of interrupt.
Driver should also register several functions used by IOCTL calls (start chip,
stop chip, set bit rate for example). These functions are assigned through
can_chip_ops
structure.
1.3.2. Frame Transmission¶
Controller is informed about new message to be transferred by frameworking
posting “worker_sem” semaphore. Function canque_test_outslot()
can then be used to obtain the highest priority ready slot from the framework.
- CALLING SEQUENCE:
int canque_test_outslot( struct canque_ends_t *qends, struct canque_edge_t **qedgep, struct canque_slot_t **slotp );
- DESCRIPTION:
Tests and retrieves oldest ready slot from the highest priority active edge (priority class).
The slot can be subsequently put into controller’s HW buffer and sent to
the network. Function canque_test_outslot()
does not free space
in FIFO queue but still holds the space for the slot until confirmed the
transmission was done. The controller should inform the framework by
calling canque_free_outslot()
function.
- CALLING SEQUENCE:
int canque_free_outslot( struct canque_ends_t *qends, struct canque_edge_t *qedge, struct canque_slot_t *slot );
- DESCRIPTION:
Releases processed slot previously acquired by
canque_test_outslot()
call.
The framework also provides a unique feature to pushback slots back to the correct FIFO and schedule the slot later processing. This is useful in case of transmission abort. The abort might be used when some later scheduled low-priority frame occupies the hardware TX buffer, which is urgently demanded for a higher priority pending message from other FIFO for example.
- CALLING SEQUENCE:
int canque_again_outslot( struct canque_ends_t *qends, struct canque_edge_t *qedge, struct canque_slot_t *slot );
- DESCRIPTION:
Reschedules slot previously acquired by
canque_test_outslot()
function call for second time processing.
Previously described function canque_test_outslot()
already
takes the slot from the FIFO. This is not convenient in case there is no
free space in controller’s hardware TX buffers. We would rather just
check whether there is some pending message from higher priority class
compared to priority classes presented in buffers. This can be done
with canque_pending_outslot_prio()
.
- CALLING SEQUENCE:
int canque_pending_outslot_prio( struct canque_ends_t *qends, int prio_min );
- DESCRIPTION:
Tests whether there is ready slot for given ends and minimum priority to be considered. Negative value informs this is not a case, positive value informs about the available slot priority class.
1.3.3. Frame Reception¶
Upon successful frame reception, the controller has to pass the frame
to FIFO edges. This is done with canque_filter_frame2edges()
- CALLING SEQUENCE:
int canque_filter_frame2edges( struct canque_ends_t *qends, struct canque_edge_t *src_edge, struct can_frame *frame, unsigned int flags2add );
- DESCRIPTION:
Sends message (frame) defined by “frame” argument to all outgoing edges connected to given ends (“qends”) with additional flags “flags2add”. Argument “src_edge” defined optional source edge for echo detection.
1.4. Registering CAN Bus¶
#include <dev/can/can-bus.h>
#include <dev/can/controller-dependent.h>
Headers mentioned above are required to use the described functions. Header
<dev/can/controller-dependent.h>
represents controller specific
header.
Once initialized (can_chip
structure allocated
and obtained from a controller specific function), the device can be
registered into /dev namespace by can_bus_register()
function.
- CALLING SEQUENCE:
int can_bus_register( struct can_bus *bus, const char *bus_path );
- DESCRIPTION:
Registers CAN devices in structure
can_bus
to /dev namespace with pathbus_path
. The path may follow standard /dev/canX naming, or it can be a different name selected by the user/BSP. Structurecan_bus
represents one CAN device and is defined as:struct can_bus { struct can_chip *chip; };
Device can be opened by
open()
function once registered into /dev namespace.
1.4.1. Example¶
The entire process of initialization and registration is demonstrated on virtual
CAN controller in the code below. Note that the user has to specify the path
to which the controller is registered, and this path has to be unique. Chip
specific function xxx_initialize()
may also have different input parameters
for different chips or can even have a different name according to chip
specific implementation.
#include <dev/can/can-bus.h>
#include <dev/can/can-virtual.h>
/* Allocate can_bus structure */
struct can_bus bus = malloc( sizeof( struct can_bus ) );
/* Initialize virtual CAN controller */
bus->chip = virtual_initialize();
/* Register controller as dev/can0, returns 0 on success */
int ret = can_bus_register( bus, "dev/can0" );