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). Field buffer_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. Fields id and flags 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 and RTEMS_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 and RTEMS_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 or CAN_BITTIME_TYPE_DATA), field “from” determines source of bit timing values (CAN_BITTIME_FROM_BITRATE or CAN_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 or CAN_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, and

  • CAN_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 to can_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, and

  • CAN_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 by can_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 by count 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, and

  • CAN_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 path bus_path. The path may follow standard /dev/canX naming, or it can be a different name selected by the user/BSP. Structure can_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" );