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. RTEMS CAN API¶
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.1.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.1.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 by
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
structure.struct can_queue { uint8_t direction; uint8_t priority; uint8_t dlen_max; uint8_t buffer_size; struct can_filter filter; };
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.1.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.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.
Field “type” determines bit timing to be set (
CAN_BITTIME_TYPE_NOMINAL
orCAN_BITTIME_TYPE_DATA
).
1.1.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.1.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.1.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 on it. Read and write calls from other applications return error in that case.
1.1.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.1.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.
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_FDF
,CAN_FRAME_BRS
,CAN_FRAME_ESI
,CAN_FRAME_ECHO
,CAN_FRAME_LOCAL
andCAN_FRAME_ERR
.
Extended frame format (CAN_FRAME_IDE
) is forced automatically if
identifier exceeds 11 bits.
1.1.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_framelen()
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.1.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. Driver Interface¶
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.
For examples of CAN controller’s driver see:
1.2.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 “chip->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 bitrate for example). These functions are assigned throught
can_chip_ops
structure.
1.2.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.2.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.3. Registering CAN bus¶
Once initialized (can_chip
structure allocated
and obtained), 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.