forked from Imagelibrary/rtems
cpukit: add support for common CAN/CAN FD stack
This adds support for common full-featured CAN/CAN FD stack to RTEMS. The API is provided in form the form of the POSIX character driver with each CAN controller (chip) registered as node into “/dev” namespace. 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. 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). Both blocking and nonblocking mode is supported. Signed-off-by: Michal Lenc <michallenc@seznam.cz> Co-authored-by: Pavel Pisa <pisa@cmp.felk.cvut.cz>
This commit is contained in:
committed by
Gedare Bloom
parent
cc4123136b
commit
c3f4e215b1
250
cpukit/dev/can/can-bittiming.c
Normal file
250
cpukit/dev/can/can-bittiming.c
Normal file
@@ -0,0 +1,250 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup CANFDStack
|
||||
*
|
||||
* @brief This source file is part of CAN/CAN FD bus common support. It
|
||||
* implements bit timing calculation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
|
||||
* Implementation is based on original LinCAN - Linux CAN bus driver
|
||||
* Part of OrtCAN project https://ortcan.sourceforge.net/
|
||||
* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://control.fel.cvut.cz>
|
||||
* Copyright (C) 2002-2024 Pavel Pisa <pisa@cmp.felk.cvut.cz>
|
||||
* Copyright (C) 2005 Stanislav Marek
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <dev/can/can.h>
|
||||
#include <dev/can/can-devcommon.h>
|
||||
|
||||
#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */
|
||||
#define CAN_CALC_SYNC_SEG 1
|
||||
|
||||
static int can_update_sample_point(
|
||||
const struct rtems_can_bittiming_const *btc,
|
||||
unsigned int sample_point_nominal,
|
||||
unsigned int tseg,
|
||||
unsigned int *tseg1_ptr,
|
||||
unsigned int *tseg2_ptr,
|
||||
unsigned int *sample_point_error_ptr
|
||||
)
|
||||
{
|
||||
unsigned int sample_point_error;
|
||||
unsigned int best_sample_point_error;
|
||||
unsigned int sample_point;
|
||||
unsigned int best_sample_point;
|
||||
unsigned int tseg1;
|
||||
unsigned int tseg2;
|
||||
int i;
|
||||
|
||||
best_sample_point_error = UINT_MAX;
|
||||
sample_point = 0;
|
||||
best_sample_point = 0;
|
||||
|
||||
for ( i = 0; i <= 1; i++ ) {
|
||||
tseg2 = tseg + CAN_CALC_SYNC_SEG - (sample_point_nominal *
|
||||
( tseg + CAN_CALC_SYNC_SEG ) ) / 1000 - i;
|
||||
|
||||
if ( tseg2 < btc->tseg2_min ) {
|
||||
tseg2 = btc->tseg2_min;
|
||||
} else if ( tseg2 > btc->tseg2_max ) {
|
||||
tseg2 = btc->tseg2_max;
|
||||
}
|
||||
|
||||
tseg1 = tseg - tseg2;
|
||||
if ( tseg1 > btc->tseg1_max ) {
|
||||
tseg1 = btc->tseg1_max;
|
||||
tseg2 = tseg - tseg1;
|
||||
}
|
||||
|
||||
sample_point = 1000 * ( tseg + CAN_CALC_SYNC_SEG - tseg2 ) /
|
||||
( tseg + CAN_CALC_SYNC_SEG );
|
||||
|
||||
sample_point_error = abs( sample_point_nominal - sample_point );
|
||||
|
||||
if ( ( sample_point <= sample_point_nominal ) &&
|
||||
( sample_point_error < best_sample_point_error ) ) {
|
||||
|
||||
best_sample_point = sample_point;
|
||||
best_sample_point_error = sample_point_error;
|
||||
*tseg1_ptr = tseg1;
|
||||
*tseg2_ptr = tseg2;
|
||||
}
|
||||
}
|
||||
|
||||
if ( sample_point_error_ptr )
|
||||
*sample_point_error_ptr = best_sample_point_error;
|
||||
|
||||
return best_sample_point;
|
||||
}
|
||||
|
||||
int rtems_can_bitrate2bittiming(
|
||||
struct rtems_can_chip *chip,
|
||||
struct rtems_can_bittiming *bt,
|
||||
const struct rtems_can_bittiming_const *btc
|
||||
)
|
||||
{
|
||||
unsigned int bitrate;
|
||||
unsigned int bitrate_error;
|
||||
unsigned int best_bitrate_error;
|
||||
unsigned int sample_point_error;
|
||||
unsigned int best_sample_point_error;
|
||||
unsigned int sample_point_nominal;
|
||||
unsigned int best_tseg;
|
||||
unsigned int best_brp;
|
||||
unsigned int brp;
|
||||
unsigned int tsegall;
|
||||
unsigned int tseg;
|
||||
unsigned int tseg1;
|
||||
unsigned int tseg2;
|
||||
uint64_t v64;
|
||||
|
||||
best_bitrate_error = UINT_MAX;
|
||||
best_sample_point_error = UINT_MAX;
|
||||
best_tseg = 0;
|
||||
best_brp = 0;
|
||||
tseg1 = 0;
|
||||
tseg2 = 0;
|
||||
|
||||
sample_point_nominal = bt->sample_point;
|
||||
|
||||
/* Use CiA recommended sample points */
|
||||
if ( bt->sample_point == 0 ) {
|
||||
if ( bt->bitrate > 800000 ) {
|
||||
sample_point_nominal = 750;
|
||||
} else if ( bt->bitrate > 500000 ) {
|
||||
sample_point_nominal = 800;
|
||||
} else {
|
||||
sample_point_nominal = 875;
|
||||
}
|
||||
}
|
||||
|
||||
/* tseg even = round down, odd = round up */
|
||||
|
||||
for (
|
||||
tseg = ( btc->tseg1_max + btc->tseg2_max ) * 2 + 1;
|
||||
tseg >= ( btc->tseg1_min + btc->tseg2_min ) * 2;
|
||||
tseg--
|
||||
) {
|
||||
tsegall = CAN_CALC_SYNC_SEG + tseg / 2;
|
||||
|
||||
/* Compute all possible tseg choices (tseg = tseg1 + tseg2) */
|
||||
brp = chip->freq / (tsegall * bt->bitrate) + tseg % 2;
|
||||
|
||||
/* choose brp step which is possible in system */
|
||||
brp = (brp / btc->brp_inc) * btc->brp_inc;
|
||||
if ( ( brp < btc->brp_min ) || ( brp > btc->brp_max ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bitrate = chip->freq / ( brp * tsegall );
|
||||
bitrate_error = abs( bt->bitrate - bitrate );
|
||||
|
||||
/* tseg brp biterror */
|
||||
if ( bitrate_error > best_bitrate_error ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* reset sample point error if we have a better bitrate */
|
||||
if ( bitrate_error < best_bitrate_error )
|
||||
best_sample_point_error = UINT_MAX;
|
||||
|
||||
can_update_sample_point(
|
||||
btc,
|
||||
sample_point_nominal,
|
||||
tseg / 2,
|
||||
&tseg1,
|
||||
&tseg2,
|
||||
&sample_point_error
|
||||
);
|
||||
|
||||
if ( sample_point_error > best_sample_point_error ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
best_sample_point_error = sample_point_error;
|
||||
best_bitrate_error = bitrate_error;
|
||||
best_tseg = tseg / 2;
|
||||
best_brp = brp;
|
||||
|
||||
if ( ( bitrate_error == 0 ) && ( sample_point_error == 0 ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( best_bitrate_error != 0 ) {
|
||||
v64 = (uint64_t) best_bitrate_error * 1000;
|
||||
v64 = v64 / bt->bitrate;
|
||||
bitrate_error = (uint32_t) v64;
|
||||
if ( bitrate_error > CAN_CALC_MAX_ERROR ) {
|
||||
return -EDOM;
|
||||
}
|
||||
}
|
||||
|
||||
/* real sample point */
|
||||
bt->sample_point = can_update_sample_point(
|
||||
btc,
|
||||
sample_point_nominal,
|
||||
best_tseg,
|
||||
&tseg1,
|
||||
&tseg2,
|
||||
NULL
|
||||
);
|
||||
|
||||
v64 = (uint64_t) best_brp * 1000 * 1000 * 1000;
|
||||
v64 = v64 / chip->freq;
|
||||
bt->tq = (uint32_t) v64;
|
||||
bt->prop_seg = tseg1 / 2;
|
||||
bt->phase_seg1 = tseg1 - bt->prop_seg;
|
||||
bt->phase_seg2 = tseg2;
|
||||
|
||||
/* check for sjw user settings */
|
||||
if ( ( bt->sjw == 0 ) || ( btc->sjw_max == 0 ) ) {
|
||||
bt->sjw = 1;
|
||||
} else {
|
||||
/* bt->sjw is at least 1 -> sanitize upper bound to sjw_max */
|
||||
if ( bt->sjw > btc->sjw_max ) {
|
||||
bt->sjw = btc->sjw_max;
|
||||
}
|
||||
/* bt->sjw must not be higher than tseg2 */
|
||||
if ( tseg2 < bt->sjw ) {
|
||||
bt->sjw = tseg2;
|
||||
}
|
||||
}
|
||||
|
||||
bt->brp = best_brp;
|
||||
|
||||
/* Calculate real bitrate */
|
||||
bt->bitrate = chip->freq / ( bt->brp *
|
||||
( CAN_CALC_SYNC_SEG + tseg1 + tseg2 ) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
826
cpukit/dev/can/can-bus.c
Normal file
826
cpukit/dev/can/can-bus.c
Normal file
@@ -0,0 +1,826 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup CANFDStack
|
||||
*
|
||||
* @brief This file is part of CAN/CAN FD bus common support
|
||||
* and implements common IO operations.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
|
||||
* Implementation is based on original LinCAN - Linux CAN bus driver
|
||||
* Part of OrtCAN project https://ortcan.sourceforge.net/
|
||||
* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://control.fel.cvut.cz>
|
||||
* Copyright (C) 2002-2024 Pavel Pisa <pisa@cmp.felk.cvut.cz>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <rtems/imfs.h>
|
||||
#include <rtems/malloc.h>
|
||||
#include <rtems/timespec.h>
|
||||
#include <rtems/score/basedefs.h>
|
||||
|
||||
#include <dev/can/can-helpers.h>
|
||||
#include <dev/can/can-devcommon.h>
|
||||
#include <dev/can/can.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static rtems_mutex canuser_manipulation_lock = RTEMS_MUTEX_INITIALIZER(
|
||||
"canuser_manipulation_lock"
|
||||
);
|
||||
|
||||
static int can_bus_open(
|
||||
rtems_libio_t *iop,
|
||||
const char *path,
|
||||
int oflag,
|
||||
mode_t mode
|
||||
);
|
||||
static int can_bus_close( rtems_libio_t *iop );
|
||||
static ssize_t can_bus_read( rtems_libio_t *iop, void *buffer, size_t count );
|
||||
static ssize_t can_bus_write(
|
||||
rtems_libio_t *iop,
|
||||
const void *buffer,
|
||||
size_t count
|
||||
);
|
||||
static int can_bus_ioctl(
|
||||
rtems_libio_t *iop,
|
||||
ioctl_command_t request,
|
||||
void *buffer
|
||||
);
|
||||
|
||||
static struct rtems_can_user *can_bus_get_user( rtems_libio_t *iop )
|
||||
{
|
||||
struct rtems_can_user *canuser = iop->data1;
|
||||
if ( !canuser || ( canuser->magic != RTEMS_CAN_USER_MAGIC ) ) {
|
||||
rtems_set_errno_and_return_value( ENODEV, NULL );
|
||||
}
|
||||
|
||||
if ( canuser->bus->chip == NULL ) {
|
||||
rtems_set_errno_and_return_value( EIO, NULL );
|
||||
}
|
||||
|
||||
return canuser;
|
||||
}
|
||||
|
||||
static int can_bus_ioctl_poll_tx_ready(
|
||||
struct rtems_can_queue_ends_user_t *qends_user,
|
||||
struct timespec *ts
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_ends *qends = &qends_user->base;
|
||||
rtems_interval timeout;
|
||||
bool nowait;
|
||||
struct timespec curr;
|
||||
struct timespec final;
|
||||
struct timespec towait;
|
||||
int ret;
|
||||
|
||||
ret = rtems_can_queue_test_inslot( qends );
|
||||
if ( ret < 0 ) {
|
||||
/* No message available */
|
||||
timeout = RTEMS_NO_TIMEOUT;
|
||||
nowait = false;
|
||||
if ( ts != NULL ) {
|
||||
/* Get absolute monotonic final time */
|
||||
clock_gettime( CLOCK_MONOTONIC, &final );
|
||||
rtems_timespec_add_to( &final, ts );
|
||||
}
|
||||
|
||||
do {
|
||||
if ( ts != NULL ) {
|
||||
/* Check current monotonic time and calculate new timeout based
|
||||
* on the difference between final time and current time.
|
||||
*/
|
||||
clock_gettime( CLOCK_MONOTONIC, &curr );
|
||||
rtems_timespec_subtract( &curr, &final, &towait );
|
||||
if ( towait.tv_sec < 0 ) {
|
||||
nowait = true;
|
||||
} else {
|
||||
timeout = rtems_timespec_to_ticks( &towait );
|
||||
}
|
||||
}
|
||||
|
||||
if ( nowait ) {
|
||||
ret = rtems_binary_semaphore_try_wait( &qends_user->sem_write );
|
||||
} else {
|
||||
ret = rtems_binary_semaphore_wait_timed_ticks(
|
||||
&qends_user->sem_write,
|
||||
timeout
|
||||
);
|
||||
}
|
||||
if ( ret != 0 ) {
|
||||
ret = -ETIME;
|
||||
break;
|
||||
}
|
||||
ret = rtems_can_queue_test_inslot( qends );
|
||||
} while ( ret < 0 );
|
||||
|
||||
rtems_binary_semaphore_post( &qends_user->sem_write );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int can_bus_ioctl_poll_rx_avail(
|
||||
struct rtems_can_queue_ends_user_t *qends_user,
|
||||
struct timespec *ts
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_ends *qends = &qends_user->base;
|
||||
rtems_interval timeout;
|
||||
bool nowait;
|
||||
struct timespec curr;
|
||||
struct timespec final;
|
||||
struct timespec towait;
|
||||
int ret;
|
||||
|
||||
ret = rtems_can_queue_pending_outslot_prio( qends, 0 );
|
||||
if ( ret < 0 ) {
|
||||
/* No message available */
|
||||
timeout = RTEMS_NO_TIMEOUT;
|
||||
nowait = false;
|
||||
if ( ts != NULL ) {
|
||||
/* Get absolute monotonic final time */
|
||||
clock_gettime( CLOCK_MONOTONIC, &final );
|
||||
rtems_timespec_add_to( &final, ts );
|
||||
}
|
||||
|
||||
do {
|
||||
if ( ts != NULL ) {
|
||||
/* Check current monotonic time and calculate new timeout based
|
||||
* on the difference between final time and current time.
|
||||
*/
|
||||
clock_gettime( CLOCK_MONOTONIC, &curr );
|
||||
rtems_timespec_subtract( &curr, &final, &towait );
|
||||
if ( towait.tv_sec < 0 ) {
|
||||
nowait = true;
|
||||
} else {
|
||||
timeout = rtems_timespec_to_ticks( &towait );
|
||||
}
|
||||
}
|
||||
|
||||
if ( nowait ) {
|
||||
ret = rtems_binary_semaphore_try_wait( &qends_user->sem_read );
|
||||
} else {
|
||||
ret = rtems_binary_semaphore_wait_timed_ticks(
|
||||
&qends_user->sem_read,
|
||||
timeout
|
||||
);
|
||||
}
|
||||
if ( ret != 0 ) {
|
||||
ret = -ETIME;
|
||||
break;
|
||||
}
|
||||
ret = rtems_can_queue_pending_outslot_prio( qends, 0 );
|
||||
} while ( ret < 0 );
|
||||
|
||||
rtems_binary_semaphore_post( &qends_user->sem_read );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int can_bus_ioctl_create_queue(
|
||||
struct rtems_can_queue_param queue,
|
||||
struct rtems_can_chip *chip,
|
||||
struct rtems_can_queue_ends *qends
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_edge *new_edge = NULL;
|
||||
struct rtems_can_queue_ends *input_ends = NULL;
|
||||
struct rtems_can_queue_ends *output_ends = NULL;
|
||||
|
||||
/* Set edge direction according to input parameter */
|
||||
if ( queue.direction == RTEMS_CAN_QUEUE_RX ) {
|
||||
input_ends = &chip->qends_dev->base;
|
||||
output_ends = qends;
|
||||
} else if ( queue.direction == RTEMS_CAN_QUEUE_TX ) {
|
||||
input_ends = qends;
|
||||
output_ends = &chip->qends_dev->base;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check whether correct dlen_max and buffer_size is to be set. */
|
||||
if (
|
||||
queue.dlen_max > CAN_FRAME_MAX_DLEN ||
|
||||
queue.dlen_max < 0 ||
|
||||
queue.buffer_size < 0
|
||||
) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ( queue.dlen_max == 0 ) {
|
||||
queue.dlen_max = chip->capabilities & RTEMS_CAN_CHIP_CAPABILITIES_FD ?
|
||||
CAN_FRAME_FD_DLEN : CAN_FRAME_STANDARD_DLEN;
|
||||
}
|
||||
|
||||
if ( queue.buffer_size == 0 ) {
|
||||
queue.buffer_size = RTEMS_CAN_FIFO_SIZE;
|
||||
}
|
||||
|
||||
/* Check whether correct queue priority is to be set. */
|
||||
if ( queue.priority < 0 || queue.priority > RTEMS_CAN_QUEUE_PRIO_NR ) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Create new edge */
|
||||
new_edge = rtems_can_queue_new_edge_kern( queue.buffer_size, queue.dlen_max );
|
||||
if ( new_edge == NULL ) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Copy filter and assign edge priority */
|
||||
new_edge->filter = queue.filter;
|
||||
new_edge->edge_prio = queue.priority;
|
||||
|
||||
/* And connect edge. This should not fail. */
|
||||
rtems_can_queue_connect_edge( new_edge, input_ends, output_ends );
|
||||
rtems_can_queue_edge_decref( new_edge );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_bus_open(
|
||||
rtems_libio_t *iop,
|
||||
const char *path,
|
||||
int oflag,
|
||||
mode_t mode
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_ends_user_t *qends_user;
|
||||
struct rtems_can_queue_ends *qends;
|
||||
struct rtems_can_queue_edge *edge4read = NULL;
|
||||
struct rtems_can_queue_edge *edge4write = NULL;
|
||||
struct rtems_can_chip *chip;
|
||||
struct rtems_can_user *canuser;
|
||||
int can_frame_dlen;
|
||||
int sc;
|
||||
|
||||
struct rtems_can_bus *bus = IMFS_generic_get_context_by_iop( iop );
|
||||
if ( bus == NULL ) {
|
||||
rtems_set_errno_and_return_minus_one( ENODEV );
|
||||
}
|
||||
|
||||
chip = bus->chip;
|
||||
|
||||
atomic_fetch_add( &chip->used, 1 );
|
||||
|
||||
canuser = ( struct rtems_can_user * )malloc( sizeof( struct rtems_can_user ) );
|
||||
if ( canuser == NULL ) {
|
||||
atomic_fetch_sub( &chip->used, 1 );
|
||||
rtems_set_errno_and_return_minus_one( ENOMEM );
|
||||
}
|
||||
|
||||
qends_user = ( struct rtems_can_queue_ends_user_t * )malloc( sizeof( struct rtems_can_queue_ends_user_t ) );
|
||||
if ( qends_user == NULL ) {
|
||||
free( canuser );
|
||||
atomic_fetch_sub( &chip->used, 1 );
|
||||
rtems_set_errno_and_return_minus_one( ENOMEM );
|
||||
}
|
||||
|
||||
canuser->flags = 0;
|
||||
canuser->bus = bus;
|
||||
canuser->magic = RTEMS_CAN_USER_MAGIC;
|
||||
|
||||
iop->data1 = canuser;
|
||||
|
||||
qends = &qends_user->base;
|
||||
rtems_can_queue_ends_init_user( qends_user );
|
||||
canuser->qends_user = qends_user;
|
||||
|
||||
can_frame_dlen = chip->capabilities & RTEMS_CAN_CHIP_CAPABILITIES_FD ?
|
||||
CAN_FRAME_FD_DLEN : CAN_FRAME_STANDARD_DLEN;
|
||||
|
||||
rtems_mutex_lock( &canuser_manipulation_lock );
|
||||
TAILQ_INSERT_TAIL( &chip->can_users, canuser, peers );
|
||||
rtems_mutex_unlock( &canuser_manipulation_lock );
|
||||
|
||||
edge4write = rtems_can_queue_new_edge_kern( RTEMS_CAN_FIFO_SIZE, can_frame_dlen );
|
||||
sc = rtems_can_queue_connect_edge( edge4write, qends, &chip->qends_dev->base );
|
||||
if ( sc < 0 )
|
||||
goto no_qedge;
|
||||
|
||||
edge4read = rtems_can_queue_new_edge_kern( RTEMS_CAN_FIFO_SIZE, can_frame_dlen );
|
||||
sc = rtems_can_queue_connect_edge( edge4read, &chip->qends_dev->base, qends );
|
||||
if ( sc < 0 )
|
||||
goto no_qedge;
|
||||
|
||||
rtems_can_queue_edge_decref( edge4write );
|
||||
rtems_can_queue_edge_decref( edge4read );
|
||||
|
||||
return 0;
|
||||
|
||||
no_qedge:
|
||||
if ( edge4write != NULL ) {
|
||||
rtems_can_queue_edge_decref( edge4write );
|
||||
}
|
||||
|
||||
if ( edge4read != NULL ) {
|
||||
rtems_can_queue_edge_decref( edge4read );
|
||||
}
|
||||
rtems_mutex_lock( &canuser_manipulation_lock );
|
||||
TAILQ_REMOVE( &chip->can_users, canuser, peers );
|
||||
rtems_mutex_unlock( &canuser_manipulation_lock );
|
||||
canuser->qends_user = NULL;
|
||||
rtems_can_queue_ends_dispose_kern( qends, chip->close_nonblock );
|
||||
|
||||
free( canuser );
|
||||
atomic_fetch_sub( &chip->used, 1 );
|
||||
rtems_set_errno_and_return_minus_one( ENOMEM );
|
||||
}
|
||||
|
||||
static int can_bus_close( rtems_libio_t *iop )
|
||||
{
|
||||
struct rtems_can_user *canuser;
|
||||
struct rtems_can_queue_ends_user_t *qends_user;
|
||||
struct rtems_can_queue_ends *qends;
|
||||
struct rtems_can_chip *chip;
|
||||
|
||||
canuser = iop->data1;
|
||||
|
||||
if ( !canuser || ( canuser->magic != RTEMS_CAN_USER_MAGIC ) ) {
|
||||
rtems_set_errno_and_return_minus_one( ENODEV );
|
||||
}
|
||||
|
||||
chip = canuser->bus->chip;
|
||||
if ( chip == NULL ) {
|
||||
rtems_set_errno_and_return_minus_one( EIO );
|
||||
}
|
||||
|
||||
qends_user = canuser->qends_user;
|
||||
qends = &qends_user->base;
|
||||
|
||||
rtems_mutex_lock( &canuser_manipulation_lock );
|
||||
TAILQ_REMOVE( &chip->can_users, canuser, peers );
|
||||
rtems_mutex_unlock( &canuser_manipulation_lock );
|
||||
canuser->qends_user = NULL;
|
||||
rtems_can_queue_ends_dispose_kern( qends, chip->close_nonblock );
|
||||
|
||||
free( canuser );
|
||||
|
||||
atomic_fetch_sub( &chip->used, 1 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t can_bus_read( rtems_libio_t *iop, void *buffer, size_t count )
|
||||
{
|
||||
struct rtems_can_queue_ends_user_t *qends_user;
|
||||
struct rtems_can_queue_ends *qends;
|
||||
struct rtems_can_queue_edge *qedge;
|
||||
struct rtems_can_queue_slot *slot;
|
||||
struct rtems_can_user *canuser;
|
||||
struct rtems_can_chip *chip;
|
||||
size_t frame_len;
|
||||
const size_t frame_header_len = sizeof( struct can_frame_header );
|
||||
int ret;
|
||||
|
||||
canuser = can_bus_get_user( iop );
|
||||
if ( canuser == NULL ) {
|
||||
/* Correct errno is already set in can_bus_get_user function. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
chip = canuser->bus->chip;
|
||||
|
||||
if ( count < frame_header_len ) {
|
||||
rtems_set_errno_and_return_minus_one( EINVAL );
|
||||
}
|
||||
|
||||
qends_user = canuser->qends_user;
|
||||
qends = &qends_user->base;
|
||||
|
||||
ret = rtems_can_queue_test_outslot( qends, &qedge, &slot );
|
||||
if ( ret < 0 ) {
|
||||
if ( rtems_libio_iop_is_no_delay( iop ) ) {
|
||||
rtems_set_errno_and_return_minus_one( EAGAIN );
|
||||
}
|
||||
|
||||
if ( rtems_can_test_bit( RTEMS_CAN_CHIP_RUNNING, &chip->flags ) == 0) {
|
||||
/* Chip is not running */
|
||||
rtems_set_errno_and_return_minus_one( EPERM );
|
||||
}
|
||||
|
||||
do {
|
||||
rtems_binary_semaphore_wait( &qends_user->sem_read );
|
||||
ret = rtems_can_queue_test_outslot( qends, &qedge, &slot );
|
||||
} while ( ret < 0 );
|
||||
|
||||
rtems_binary_semaphore_post( &qends_user->sem_read );
|
||||
}
|
||||
|
||||
frame_len = can_framesize( ( struct can_frame *)&slot->frame );
|
||||
if ( count > frame_len ) {
|
||||
count = frame_len;
|
||||
}
|
||||
|
||||
memcpy( buffer, &slot->frame, count );
|
||||
|
||||
if ( count < frame_len ) {
|
||||
rtems_can_queue_push_back_outslot( qends, qedge, slot );
|
||||
rtems_set_errno_and_return_minus_one( EMSGSIZE );
|
||||
} else {
|
||||
rtems_can_queue_free_outslot( qends, qedge, slot );
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t can_bus_write(
|
||||
rtems_libio_t *iop,
|
||||
const void *buffer,
|
||||
size_t count
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_ends_user_t *qends_user;
|
||||
struct rtems_can_queue_ends *qends;
|
||||
struct rtems_can_queue_edge *qedge;
|
||||
struct rtems_can_queue_slot *slot;
|
||||
struct rtems_can_user *canuser;
|
||||
struct rtems_can_chip *chip;
|
||||
struct can_frame_header frame_header;
|
||||
const size_t frame_header_len = sizeof( struct can_frame_header );
|
||||
size_t frame_bytes;
|
||||
int ret;
|
||||
|
||||
canuser = can_bus_get_user( iop );
|
||||
if ( canuser == NULL ) {
|
||||
/* Correct errno is already set in can_bus_get_user function. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
chip = canuser->bus->chip;
|
||||
if ( rtems_can_test_bit( RTEMS_CAN_CHIP_RUNNING, &chip->flags ) == 0 ) {
|
||||
/* Chip is not running */
|
||||
rtems_set_errno_and_return_minus_one( EPERM );
|
||||
}
|
||||
|
||||
if ( count < frame_header_len ) {
|
||||
rtems_set_errno_and_return_minus_one( EINVAL );
|
||||
}
|
||||
|
||||
memcpy( &frame_header, buffer, frame_header_len );
|
||||
if ( frame_header.dlen > CAN_FRAME_MAX_DLEN ) {
|
||||
rtems_set_errno_and_return_minus_one( EMSGSIZE );
|
||||
}
|
||||
|
||||
if ( count < frame_header_len + frame_header.dlen ) {
|
||||
rtems_set_errno_and_return_minus_one( EMSGSIZE );
|
||||
}
|
||||
|
||||
frame_bytes = frame_header_len + frame_header.dlen;
|
||||
qends_user = canuser->qends_user;
|
||||
qends = &qends_user->base;
|
||||
|
||||
if ( ( ret = rtems_can_queue_get_inslot_for_prio( qends, &qedge, &slot, &frame_header, 0, 2 ) ) < 0 ) {
|
||||
if ( ret < -1 ) {
|
||||
rtems_set_errno_and_return_minus_one( EIO );
|
||||
}
|
||||
|
||||
if ( rtems_libio_iop_is_no_delay( iop ) ) {
|
||||
rtems_set_errno_and_return_minus_one( EAGAIN );
|
||||
}
|
||||
|
||||
do {
|
||||
rtems_binary_semaphore_wait( &qends_user->sem_write );
|
||||
if ( rtems_can_test_bit( RTEMS_CAN_CHIP_RUNNING, &chip->flags ) == 0 ) {
|
||||
rtems_binary_semaphore_post( &qends_user->sem_write );
|
||||
rtems_set_errno_and_return_minus_one( EPERM );
|
||||
}
|
||||
ret = rtems_can_queue_get_inslot_for_prio( qends, &qedge, &slot, &frame_header, 0, 2 );
|
||||
if ( ret < -1 ) {
|
||||
rtems_binary_semaphore_post( &qends_user->sem_write );
|
||||
rtems_set_errno_and_return_minus_one( EIO );
|
||||
}
|
||||
} while ( ret < 0 );
|
||||
|
||||
rtems_binary_semaphore_post( &qends_user->sem_write );
|
||||
}
|
||||
|
||||
if ( frame_header.dlen > qedge->fifo.max_data_length ) {
|
||||
rtems_can_queue_abort_inslot( qends, qedge, slot );
|
||||
rtems_set_errno_and_return_minus_one( EMSGSIZE );
|
||||
}
|
||||
|
||||
memcpy( &slot->frame, buffer, frame_bytes );
|
||||
|
||||
/* Force extended frame format if id exceeds 11 bits */
|
||||
if ( slot->frame.header.can_id & ~CAN_FRAME_BFF_ID_MASK & CAN_FRAME_EFF_ID_MASK ) {
|
||||
slot->frame.header.flags |= CAN_FRAME_IDE;
|
||||
}
|
||||
|
||||
rtems_can_queue_put_inslot( qends, qedge, slot );
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int can_bus_ioctl(
|
||||
rtems_libio_t *iop,
|
||||
ioctl_command_t command,
|
||||
void *arg
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_ends_user_t *qends_user;
|
||||
struct rtems_can_queue_ends *qends;
|
||||
struct rtems_can_user *canuser;
|
||||
struct rtems_can_chip *chip;
|
||||
struct timespec ts;
|
||||
int direction;
|
||||
int ret = -EINVAL;
|
||||
|
||||
canuser = iop->data1;
|
||||
if ( !canuser || ( canuser->magic != RTEMS_CAN_USER_MAGIC ) ) {
|
||||
rtems_set_errno_and_return_minus_one( ENODEV );
|
||||
}
|
||||
|
||||
chip = canuser->bus->chip;
|
||||
if ( chip == NULL ) {
|
||||
rtems_set_errno_and_return_minus_one( EIO );
|
||||
}
|
||||
|
||||
qends_user = canuser->qends_user;
|
||||
qends = &qends_user->base;
|
||||
|
||||
switch ( command ) {
|
||||
case RTEMS_CAN_CHIP_START:
|
||||
if ( chip->chip_ops.start_chip != NULL ) {
|
||||
ret = chip->chip_ops.start_chip( chip );
|
||||
}
|
||||
break;
|
||||
|
||||
case RTEMS_CAN_CHIP_STOP:
|
||||
if ( chip->chip_ops.stop_chip != NULL ) {
|
||||
if ( arg != 0 ) {
|
||||
ts = *( struct timespec * )arg;
|
||||
ret = chip->chip_ops.stop_chip( chip, &ts );
|
||||
} else {
|
||||
ret = chip->chip_ops.stop_chip( chip, NULL );
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RTEMS_CAN_CLOSE_NONBLOCK:
|
||||
chip->close_nonblock = ( bool )arg;
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
case RTEMS_CAN_WAIT_TX_DONE:
|
||||
if ( arg != 0 ) {
|
||||
ts = *( struct timespec * )arg;
|
||||
ret = rtems_can_queue_ends_sync_all_kern( qends, &ts );
|
||||
} else {
|
||||
ret = rtems_can_queue_ends_sync_all_kern( qends, NULL );
|
||||
}
|
||||
break;
|
||||
|
||||
case RTEMS_CAN_POLL_TX_READY:
|
||||
if ( arg != 0) {
|
||||
ts = *( struct timespec * )arg;
|
||||
ret = can_bus_ioctl_poll_tx_ready( qends_user, &ts );
|
||||
} else {
|
||||
ret = can_bus_ioctl_poll_tx_ready( qends_user, NULL );
|
||||
}
|
||||
break;
|
||||
|
||||
case RTEMS_CAN_POLL_RX_AVAIL:
|
||||
if ( arg != 0) {
|
||||
ts = *( struct timespec * )arg;
|
||||
ret = can_bus_ioctl_poll_rx_avail( qends_user, &ts );
|
||||
} else {
|
||||
ret = can_bus_ioctl_poll_rx_avail( qends_user, NULL );
|
||||
}
|
||||
break;
|
||||
|
||||
case RTEMS_CAN_DISCARD_QUEUES:
|
||||
direction = ( intptr_t )arg;
|
||||
ret = 0;
|
||||
if ( ( direction & ( RTEMS_CAN_QUEUE_RX | RTEMS_CAN_QUEUE_TX ) ) == 0 ) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
if ( direction & RTEMS_CAN_QUEUE_RX ) {
|
||||
rtems_can_queue_ends_kill_outlist( qends );
|
||||
}
|
||||
|
||||
if ( direction & RTEMS_CAN_QUEUE_TX ) {
|
||||
rtems_can_queue_ends_kill_inlist( qends, 0 );
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RTEMS_CAN_FLUSH_QUEUES:
|
||||
direction = ( intptr_t )arg;
|
||||
ret = 0;
|
||||
|
||||
if ( ( direction & ( RTEMS_CAN_QUEUE_RX | RTEMS_CAN_QUEUE_TX ) ) == 0 ) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
if ( direction & RTEMS_CAN_QUEUE_RX ) {
|
||||
rtems_can_queue_ends_flush_outlist( qends );
|
||||
}
|
||||
|
||||
if ( direction & RTEMS_CAN_QUEUE_TX ) {
|
||||
rtems_can_queue_ends_flush_inlist( qends );
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RTEMS_CAN_CREATE_QUEUE:
|
||||
ret = can_bus_ioctl_create_queue(
|
||||
*( struct rtems_can_queue_param *)arg,
|
||||
chip,
|
||||
qends
|
||||
);
|
||||
break;
|
||||
|
||||
case RTEMS_CAN_SET_BITRATE:
|
||||
struct rtems_can_set_bittiming bittime =
|
||||
*( struct rtems_can_set_bittiming *)arg;
|
||||
if ( bittime.from == RTEMS_CAN_BITTIME_FROM_BITRATE ) {
|
||||
ret = chip->chip_ops.calc_bittiming(
|
||||
chip,
|
||||
bittime.type,
|
||||
&bittime.bittiming
|
||||
);
|
||||
} else if ( bittime.from == RTEMS_CAN_BITTIME_FROM_PRECOMPUTED ) {
|
||||
ret = chip->chip_ops.check_and_set_bittiming(
|
||||
chip,
|
||||
bittime.type,
|
||||
&bittime.bittiming
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case RTEMS_CAN_CHIP_SET_MODE:
|
||||
uint32_t ctrlmode = ( uintptr_t )arg;
|
||||
if ( rtems_can_test_bit( RTEMS_CAN_CHIP_RUNNING, &chip->flags ) == 1 ) {
|
||||
/* Cannot change the mode if chip has already started */
|
||||
ret = -EPERM;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ( ctrlmode & ~chip->ctrlmode_supported ) & CAN_CTRLMODE_MASK ) {
|
||||
/* Not supported mode, return error */
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
chip->ctrlmode = ctrlmode;
|
||||
ret = RTEMS_SUCCESSFUL;
|
||||
}
|
||||
break;
|
||||
|
||||
case RTEMS_CAN_GET_BITTIMING:
|
||||
struct rtems_can_get_bittiming *bittiming =
|
||||
( struct rtems_can_get_bittiming *)arg;
|
||||
ret = RTEMS_SUCCESSFUL;
|
||||
if ( bittiming->type == RTEMS_CAN_BITTIME_NOMINAL ) {
|
||||
memcpy(
|
||||
&bittiming->bittiming,
|
||||
&chip->bittiming,
|
||||
sizeof( struct rtems_can_bittiming )
|
||||
);
|
||||
memcpy(
|
||||
&bittiming->bittiming_const,
|
||||
chip->bittiming_const,
|
||||
sizeof( struct rtems_can_bittiming_const )
|
||||
);
|
||||
} else if ( bittiming->type == RTEMS_CAN_BITTIME_DATA ) {
|
||||
memcpy(
|
||||
&bittiming->bittiming,
|
||||
&chip->data_bittiming,
|
||||
sizeof( struct rtems_can_bittiming )
|
||||
);
|
||||
memcpy(
|
||||
&bittiming->bittiming_const,
|
||||
chip->data_bittiming_const,
|
||||
sizeof( struct rtems_can_bittiming_const )
|
||||
);
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case RTEMS_CAN_CHIP_GET_TIMESTAMP:
|
||||
uint64_t timestamp;
|
||||
if ( chip->chip_ops.get_chip_timestamp != NULL ) {
|
||||
ret = chip->chip_ops.get_chip_timestamp( chip, ×tamp );
|
||||
*( uint64_t * )arg = timestamp;
|
||||
}
|
||||
break;
|
||||
|
||||
case RTEMS_CAN_CHIP_STATISTICS:
|
||||
*( struct rtems_can_stats* )arg = chip->chip_stats;
|
||||
ret = RTEMS_SUCCESSFUL;
|
||||
break;
|
||||
|
||||
case RTEMS_CAN_CHIP_GET_INFO:
|
||||
if ( chip->chip_ops.get_chip_info != NULL ) {
|
||||
ret = chip->chip_ops.get_chip_info( chip, ( intptr_t )arg );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if ( chip->chip_ops.chip_ioctl != NULL ) {
|
||||
ret = chip->chip_ops.chip_ioctl( chip, command, arg );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ( ret < 0 ) {
|
||||
rtems_set_errno_and_return_minus_one( -ret );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const rtems_filesystem_file_handlers_r can_bus_handler = {
|
||||
.open_h = can_bus_open,
|
||||
.close_h = can_bus_close,
|
||||
.read_h = can_bus_read,
|
||||
.write_h = can_bus_write,
|
||||
.ioctl_h = can_bus_ioctl,
|
||||
.lseek_h = rtems_filesystem_default_lseek,
|
||||
.fstat_h = IMFS_stat,
|
||||
.ftruncate_h = rtems_filesystem_default_ftruncate,
|
||||
.fsync_h = rtems_filesystem_default_fsync_or_fdatasync,
|
||||
.fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
|
||||
.fcntl_h = rtems_filesystem_default_fcntl,
|
||||
.kqfilter_h = rtems_filesystem_default_kqfilter,
|
||||
.mmap_h = rtems_filesystem_default_mmap,
|
||||
.poll_h = rtems_filesystem_default_poll,
|
||||
.readv_h = rtems_filesystem_default_readv,
|
||||
.writev_h = rtems_filesystem_default_writev
|
||||
};
|
||||
|
||||
static void can_bus_node_destroy( IMFS_jnode_t *node )
|
||||
{
|
||||
IMFS_node_destroy_default( node );
|
||||
}
|
||||
|
||||
static const IMFS_node_control can_bus_node_control = IMFS_GENERIC_INITIALIZER(
|
||||
&can_bus_handler,
|
||||
IMFS_node_initialize_generic,
|
||||
can_bus_node_destroy
|
||||
);
|
||||
|
||||
int rtems_can_bus_notify_chip_stop( struct rtems_can_chip *chip )
|
||||
{
|
||||
struct rtems_can_user *user;
|
||||
|
||||
rtems_mutex_lock( &canuser_manipulation_lock );
|
||||
user = TAILQ_FIRST( &chip->can_users );
|
||||
|
||||
while ( user != NULL ) {
|
||||
rtems_binary_semaphore_post( &user->qends_user->sem_read );
|
||||
user = TAILQ_NEXT( user, peers );
|
||||
}
|
||||
|
||||
rtems_mutex_unlock( &canuser_manipulation_lock );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtems_can_bus_register( struct rtems_can_bus *bus, const char *bus_path )
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ( rtems_can_queue_kern_initialize() < 0 ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
TAILQ_INIT( &bus->chip->can_users );
|
||||
|
||||
ret = IMFS_make_generic_node(
|
||||
bus_path,
|
||||
S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO,
|
||||
&can_bus_node_control,
|
||||
bus
|
||||
);
|
||||
|
||||
return ret;
|
||||
}
|
||||
117
cpukit/dev/can/can-devcommon.c
Normal file
117
cpukit/dev/can/can-devcommon.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup CANFDStack
|
||||
*
|
||||
* @brief This file is part of CAN/CAN FD bus common support
|
||||
* and implements controller's side of FIFO operations.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
|
||||
* Implementation is based on original LinCAN - Linux CAN bus driver
|
||||
* Part of OrtCAN project https://ortcan.sourceforge.net/
|
||||
* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://control.fel.cvut.cz>
|
||||
* Copyright (C) 2002-2024 Pavel Pisa <pisa@cmp.felk.cvut.cz>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <rtems/score/basedefs.h>
|
||||
|
||||
#include <dev/can/can-devcommon.h>
|
||||
#include <dev/can/can-stats.h>
|
||||
|
||||
#define SEC_TO_NSEC 1000000000
|
||||
|
||||
static void can_queue_notify_chip(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge *qedge,
|
||||
int what
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_ends_dev *qends_dev = RTEMS_CONTAINER_OF(
|
||||
qends,
|
||||
struct rtems_can_queue_ends_dev,
|
||||
base
|
||||
);
|
||||
|
||||
switch ( what ) {
|
||||
case RTEMS_CAN_QUEUE_NOTIFY_PROC:
|
||||
rtems_binary_semaphore_post( &qends_dev->worker_sem );
|
||||
break;
|
||||
case RTEMS_CAN_QUEUE_NOTIFY_DEAD_WANTED:
|
||||
case RTEMS_CAN_QUEUE_NOTIFY_DEAD:
|
||||
if ( rtems_can_queue_fifo_test_and_clear_fl(
|
||||
&qedge->fifo,
|
||||
RTEMS_CAN_FIFOF_READY
|
||||
) ) {
|
||||
rtems_can_queue_edge_decref( qedge );
|
||||
}
|
||||
break;
|
||||
case RTEMS_CAN_QUEUE_NOTIFY_ATTACH:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void rtems_can_stats_reset( struct rtems_can_stats *stats )
|
||||
{
|
||||
memset( stats, 0, sizeof( *stats ) );
|
||||
}
|
||||
|
||||
int rtems_can_queue_ends_init_chip(
|
||||
struct rtems_can_chip *chip,
|
||||
const char *name
|
||||
)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = rtems_can_queue_ends_init( &chip->qends_dev->base );
|
||||
if ( ret < 0 ) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
chip->qends_dev->chip = chip;
|
||||
chip->qends_dev->base.notify = can_queue_notify_chip;
|
||||
rtems_binary_semaphore_init( &chip->qends_dev->worker_sem, name );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t rtems_can_fill_timestamp( void )
|
||||
{
|
||||
struct timespec tp;
|
||||
clock_gettime( CLOCK_MONOTONIC, &tp );
|
||||
return ( tp.tv_sec * SEC_TO_NSEC + tp.tv_nsec );
|
||||
}
|
||||
|
||||
int rtems_can_chip_start( struct rtems_can_chip *chip )
|
||||
{
|
||||
if ( chip->chip_ops.start_chip != NULL ) {
|
||||
return chip->chip_ops.start_chip( chip );
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
457
cpukit/dev/can/can-quekern.c
Normal file
457
cpukit/dev/can/can-quekern.c
Normal file
@@ -0,0 +1,457 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup CANFDStack
|
||||
*
|
||||
* @brief This file is part of CAN/CAN FD bus common support
|
||||
* and implements CAN FIFOs and generic hubs/ends
|
||||
* for chip and caracter driver interface sides.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
|
||||
* Implementation is based on original LinCAN - Linux CAN bus driver
|
||||
* Part of OrtCAN project https://ortcan.sourceforge.net/
|
||||
* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://control.fel.cvut.cz>
|
||||
* Copyright (C) 2002-2024 Pavel Pisa <pisa@cmp.felk.cvut.cz>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <rtems/malloc.h>
|
||||
#include <rtems/timespec.h>
|
||||
|
||||
#include <dev/can/can.h>
|
||||
#include <dev/can/can-helpers.h>
|
||||
#include <dev/can/can-devcommon.h>
|
||||
#include <dev/can/can-impl.h>
|
||||
|
||||
#define CAN_DEAD_FUNC_PRIORITY 120
|
||||
|
||||
#define CAN_KERN_INITIALIZED ( 1 << 0 )
|
||||
|
||||
static atomic_uint edge_num_cnt;
|
||||
|
||||
static rtems_mutex can_queue_dead_func_lock = RTEMS_MUTEX_INITIALIZER(
|
||||
"can_queue_dead_func_lock"
|
||||
);
|
||||
static rtems_binary_semaphore dead_func_sem = RTEMS_BINARY_SEMAPHORE_INITIALIZER(
|
||||
"can_queue_dead_func_sem"
|
||||
);
|
||||
|
||||
static struct rtems_can_queue_ends_list can_queue_dead_ends;
|
||||
static struct rtems_can_queue_edges_list can_queue_dead_edges;
|
||||
|
||||
static atomic_uint kern_flags;
|
||||
|
||||
static inline struct rtems_can_queue_edge *can_queue_dead_edges_cut_first( void )
|
||||
{
|
||||
struct rtems_can_queue_edge *edge;
|
||||
rtems_mutex_lock( &can_queue_dead_func_lock );
|
||||
if ( TAILQ_EMPTY( &can_queue_dead_edges ) ) {
|
||||
edge = NULL;
|
||||
} else {
|
||||
edge = TAILQ_FIRST( &can_queue_dead_edges );
|
||||
TAILQ_REMOVE( &can_queue_dead_edges, edge, input_peers );
|
||||
}
|
||||
rtems_mutex_unlock( &can_queue_dead_func_lock );
|
||||
return edge;
|
||||
}
|
||||
|
||||
static rtems_task can_queue_dead_func( rtems_task_argument arg )
|
||||
{
|
||||
struct rtems_can_queue_edge *qedge;
|
||||
struct rtems_can_queue_ends *qends;
|
||||
struct rtems_can_queue_ends *entry;
|
||||
|
||||
while ( 1 ) {
|
||||
while ( ( qedge = can_queue_dead_edges_cut_first() ) != NULL ) {
|
||||
RTEMS_DEBUG_PRINT( "Edge %d disposed\n", qedge->edge_num );
|
||||
|
||||
rtems_can_queue_fifo_done_kern( &qedge->fifo );
|
||||
free( qedge );
|
||||
}
|
||||
|
||||
rtems_mutex_lock( &can_queue_dead_func_lock );
|
||||
entry = TAILQ_FIRST( &can_queue_dead_ends );
|
||||
|
||||
/* Lock can be released there, because only one instance of can_queue_dead_tl
|
||||
* can run at once and all other functions add ends only to head.
|
||||
*/
|
||||
rtems_mutex_unlock( &can_queue_dead_func_lock );
|
||||
|
||||
while ( entry != NULL ) {
|
||||
qends = entry;
|
||||
entry = TAILQ_NEXT( qends, dead_peers );
|
||||
if ( !TAILQ_EMPTY( &qends->inlist ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( !TAILQ_EMPTY( &qends->outlist ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rtems_mutex_lock( &can_queue_dead_func_lock );
|
||||
TAILQ_REMOVE( &can_queue_dead_ends, qends, dead_peers );
|
||||
rtems_mutex_unlock( &can_queue_dead_func_lock );
|
||||
|
||||
RTEMS_DEBUG_PRINT( "Ends structure disposed\n" );
|
||||
|
||||
free( qends );
|
||||
}
|
||||
|
||||
rtems_binary_semaphore_wait( &dead_func_sem );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void can_queue_dead_func_release( void )
|
||||
{
|
||||
rtems_binary_semaphore_post( &dead_func_sem );
|
||||
}
|
||||
|
||||
void rtems_can_queue_edge_do_dead( struct rtems_can_queue_edge *qedge )
|
||||
{
|
||||
rtems_can_queue_notify_both_ends( qedge, RTEMS_CAN_QUEUE_NOTIFY_NOUSR );
|
||||
|
||||
if ( rtems_can_queue_disconnect_edge( qedge ) < 0 ) {
|
||||
RTEMS_DEBUG_PRINT( "rtems_can_queue_disconnect_edge failed.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
rtems_mutex_lock( &can_queue_dead_func_lock );
|
||||
TAILQ_INSERT_TAIL( &can_queue_dead_edges, qedge, input_peers );
|
||||
rtems_mutex_unlock( &can_queue_dead_func_lock );
|
||||
can_queue_dead_func_release();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Notification callback handler for Linux userspace clients
|
||||
*
|
||||
* @param qends Pointer to the callback side ends structure.
|
||||
* @param qedge Edge which invoked notification.
|
||||
* @param what Notification type.
|
||||
*
|
||||
* @return None
|
||||
*
|
||||
*/
|
||||
static void can_queue_notify_user(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge *qedge,
|
||||
int what
|
||||
)
|
||||
{
|
||||
RTEMS_DEBUG_PRINT( "For edge %d, use %d and event %d\n",
|
||||
qedge->edge_num, (int)atomic_load( &qedge->edge_used ), what );
|
||||
|
||||
struct rtems_can_queue_ends_user_t *qends_user = RTEMS_CONTAINER_OF(
|
||||
qends,
|
||||
struct rtems_can_queue_ends_user_t,
|
||||
base
|
||||
);
|
||||
|
||||
switch ( what ) {
|
||||
case RTEMS_CAN_QUEUE_NOTIFY_EMPTY:
|
||||
rtems_binary_semaphore_post( &qends_user->sem_sync );
|
||||
if ( rtems_can_queue_fifo_test_and_clear_fl(
|
||||
&qedge->fifo,
|
||||
RTEMS_CAN_FIFOF_FREEONEMPTY
|
||||
) ) {
|
||||
rtems_can_queue_edge_decref( qedge );
|
||||
}
|
||||
break;
|
||||
case RTEMS_CAN_QUEUE_NOTIFY_SPACE:
|
||||
rtems_binary_semaphore_post( &qends_user->sem_write );
|
||||
break;
|
||||
case RTEMS_CAN_QUEUE_NOTIFY_PROC:
|
||||
rtems_binary_semaphore_post( &qends_user->sem_read );
|
||||
break;
|
||||
case RTEMS_CAN_QUEUE_NOTIFY_NOUSR:
|
||||
rtems_binary_semaphore_post( &qends_user->sem_sync );
|
||||
break;
|
||||
case RTEMS_CAN_QUEUE_NOTIFY_DEAD_WANTED:
|
||||
case RTEMS_CAN_QUEUE_NOTIFY_DEAD:
|
||||
if ( rtems_can_queue_fifo_test_and_clear_fl(
|
||||
&qedge->fifo,
|
||||
RTEMS_CAN_FIFOF_READY
|
||||
) ) {
|
||||
rtems_can_queue_edge_decref( qedge );
|
||||
}
|
||||
break;
|
||||
case RTEMS_CAN_QUEUE_NOTIFY_ATTACH:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int rtems_can_queue_ends_init_user( struct rtems_can_queue_ends_user_t *qends_user )
|
||||
{
|
||||
struct rtems_can_queue_ends *qends = &qends_user->base;
|
||||
|
||||
rtems_can_queue_ends_init( qends );
|
||||
|
||||
qends->notify = can_queue_notify_user;
|
||||
|
||||
rtems_binary_semaphore_init(
|
||||
&qends_user->sem_read,
|
||||
"can_qends_user_read_sem"
|
||||
);
|
||||
rtems_binary_semaphore_init(
|
||||
&qends_user->sem_write,
|
||||
"can_qends_user_write_sem"
|
||||
);
|
||||
rtems_binary_semaphore_init(
|
||||
&qends_user->sem_sync,
|
||||
"can_qends_user_sync_sem"
|
||||
);
|
||||
|
||||
RTEMS_DEBUG_PRINT( "Initialized\n" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtems_can_queue_sync_wait_kern(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge *qedge,
|
||||
bool nowait,
|
||||
rtems_interval timeout
|
||||
)
|
||||
{
|
||||
int ret;
|
||||
struct rtems_can_queue_ends_user_t *qends_user = RTEMS_CONTAINER_OF(
|
||||
qends,
|
||||
struct rtems_can_queue_ends_user_t,
|
||||
base
|
||||
);
|
||||
|
||||
/* Obtain semaphore only if FIFO is not empty. There may be cases where
|
||||
* qends_user is used only as RX and closed without sending a message.
|
||||
* In that case the semaphore would never be released.
|
||||
*/
|
||||
|
||||
if ( !rtems_can_queue_fifo_test_flag( &qedge->fifo, RTEMS_CAN_FIFOF_EMPTY ) ) {
|
||||
do {
|
||||
if ( nowait ) {
|
||||
ret = rtems_binary_semaphore_try_wait( &qends_user->sem_sync );
|
||||
} else {
|
||||
ret = rtems_binary_semaphore_wait_timed_ticks(
|
||||
&qends_user->sem_sync,
|
||||
timeout
|
||||
);
|
||||
}
|
||||
if ( ret != 0 ) {
|
||||
return -1;
|
||||
}
|
||||
} while (
|
||||
!rtems_can_queue_fifo_test_flag( &qedge->fifo, RTEMS_CAN_FIFOF_EMPTY )
|
||||
);
|
||||
|
||||
rtems_binary_semaphore_post( &qends_user->sem_sync );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtems_can_queue_fifo_init_kern(
|
||||
struct rtems_can_queue_fifo *fifo,
|
||||
int allocated_slot_count,
|
||||
int max_data_length
|
||||
)
|
||||
{
|
||||
int size;
|
||||
if ( allocated_slot_count == 0 ) {
|
||||
allocated_slot_count = RTEMS_CAN_FIFO_SIZE;
|
||||
}
|
||||
|
||||
if ( max_data_length == 0 ) {
|
||||
max_data_length = CAN_FRAME_MAX_DLEN;
|
||||
}
|
||||
|
||||
size = rtems_can_queue_fifo_slot_size( max_data_length ) * allocated_slot_count;
|
||||
fifo->entry = malloc( size );
|
||||
if ( fifo->entry == NULL ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fifo->allocated_slot_count = allocated_slot_count;
|
||||
fifo->max_data_length = max_data_length;
|
||||
return rtems_can_queue_fifo_init_slots( fifo );
|
||||
}
|
||||
|
||||
int rtems_can_queue_fifo_done_kern( struct rtems_can_queue_fifo *fifo )
|
||||
{
|
||||
if ( fifo->entry != NULL ) {
|
||||
free( fifo->entry );
|
||||
}
|
||||
|
||||
fifo->entry = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct rtems_can_queue_edge *rtems_can_queue_new_edge_kern(
|
||||
int allocated_slot_count,
|
||||
int max_data_length
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_edge *qedge;
|
||||
qedge = (struct rtems_can_queue_edge *)calloc( 1, sizeof( struct rtems_can_queue_edge ) );
|
||||
if ( qedge == NULL ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rtems_mutex_init( &qedge->fifo.fifo_lock, "fifo_lock" );
|
||||
if ( rtems_can_queue_fifo_init_kern(
|
||||
&qedge->fifo,
|
||||
allocated_slot_count,
|
||||
max_data_length
|
||||
) < 0 ) {
|
||||
free( qedge );
|
||||
RTEMS_DEBUG_PRINT( "Failed\n" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
atomic_store( &qedge->edge_used, 1 );
|
||||
qedge->filter.id = 0;
|
||||
qedge->filter.id_mask = 0;
|
||||
qedge->filter.flags = 0;
|
||||
qedge->filter.flags_mask = CAN_FRAME_ECHO | CAN_FRAME_TXERR | CAN_FRAME_ERR;
|
||||
qedge->edge_prio = 0;
|
||||
/* not exactly clean, but enough for debugging */
|
||||
atomic_fetch_add( &edge_num_cnt, 1 );
|
||||
qedge->edge_num = atomic_load( &edge_num_cnt );
|
||||
RTEMS_DEBUG_PRINT( "New edge %d\n", qedge->edge_num );
|
||||
return qedge;
|
||||
}
|
||||
|
||||
int rtems_can_queue_ends_sync_all_kern(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct timespec *ts
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_edge *qedge;
|
||||
struct timespec curr;
|
||||
struct timespec final;
|
||||
struct timespec towait;
|
||||
rtems_interval timeout;
|
||||
bool nowait;
|
||||
int ret;
|
||||
|
||||
timeout = RTEMS_NO_TIMEOUT;
|
||||
nowait = false;
|
||||
if ( ts != NULL ) {
|
||||
clock_gettime( CLOCK_MONOTONIC, &final );
|
||||
rtems_timespec_add_to( &final, ts );
|
||||
}
|
||||
|
||||
rtems_can_queue_for_each_inedge( qends, qedge ) {
|
||||
RTEMS_DEBUG_PRINT( "Called for edge %d\n", qedge->edge_num );
|
||||
if ( ts != NULL ) {
|
||||
clock_gettime( CLOCK_MONOTONIC, &curr );
|
||||
rtems_timespec_subtract( &curr, &final, &towait );
|
||||
if ( towait.tv_sec < 0 ) {
|
||||
nowait = true;
|
||||
} else {
|
||||
timeout = rtems_timespec_to_ticks( &towait );
|
||||
}
|
||||
}
|
||||
|
||||
ret = rtems_can_queue_sync_wait_kern( qends, qedge, nowait, timeout );
|
||||
if ( ret == -1 ) {
|
||||
rtems_can_queue_edge_decref( qedge );
|
||||
return -ETIME;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void can_queue_ends_dispose_postpone(
|
||||
struct rtems_can_queue_ends *qends
|
||||
)
|
||||
{
|
||||
rtems_mutex_lock( &can_queue_dead_func_lock );
|
||||
TAILQ_INSERT_TAIL( &can_queue_dead_ends, qends, dead_peers );
|
||||
rtems_mutex_unlock( &can_queue_dead_func_lock );
|
||||
can_queue_dead_func_release();
|
||||
}
|
||||
|
||||
int rtems_can_queue_ends_dispose_kern(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
bool nonblock
|
||||
)
|
||||
{
|
||||
int delayed;
|
||||
|
||||
rtems_can_queue_block_inlist( qends );
|
||||
rtems_can_queue_block_outlist( qends );
|
||||
|
||||
/* Wait until all pending messages in the output FIFOs are sent */
|
||||
if ( nonblock == false ) {
|
||||
rtems_can_queue_ends_sync_all_kern( qends, NULL );
|
||||
}
|
||||
|
||||
/* Finish or kill all outgoing edges listed in input_ends */
|
||||
delayed = rtems_can_queue_ends_kill_inlist( qends, 1 );
|
||||
/* Kill all incoming edges listed in output_ends */
|
||||
delayed |= rtems_can_queue_ends_kill_outlist( qends );
|
||||
|
||||
if ( delayed ) {
|
||||
can_queue_ends_dispose_postpone( qends );
|
||||
|
||||
RTEMS_DEBUG_PRINT( "Delayed\n" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
free( qends );
|
||||
RTEMS_DEBUG_PRINT( "Finished\n" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtems_can_queue_kern_initialize( void )
|
||||
{
|
||||
rtems_id dead_func_id;
|
||||
rtems_status_code sc;
|
||||
|
||||
if ( rtems_can_test_and_set_bit( CAN_KERN_INITIALIZED, &kern_flags ) == 1 ) {
|
||||
/* Already initialized */
|
||||
return 0;
|
||||
}
|
||||
|
||||
TAILQ_INIT( &can_queue_dead_ends );
|
||||
TAILQ_INIT( &can_queue_dead_edges );
|
||||
|
||||
sc = rtems_task_create(
|
||||
rtems_build_name( 'C', 'A', 'N', 'D' ),
|
||||
CAN_DEAD_FUNC_PRIORITY,
|
||||
RTEMS_MINIMUM_STACK_SIZE+0x1000,
|
||||
RTEMS_DEFAULT_MODES,
|
||||
RTEMS_DEFAULT_ATTRIBUTES,
|
||||
&dead_func_id
|
||||
);
|
||||
if ( sc != RTEMS_SUCCESSFUL )
|
||||
return -1;
|
||||
|
||||
rtems_task_start( dead_func_id, can_queue_dead_func, 0 );
|
||||
|
||||
return 0;
|
||||
}
|
||||
737
cpukit/dev/can/can-queue.c
Normal file
737
cpukit/dev/can/can-queue.c
Normal file
@@ -0,0 +1,737 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup CANFDStack
|
||||
*
|
||||
* @brief This file is part of CAN/CAN FD bus common support
|
||||
* and implements CAN FIFOs and generic hubs/ends
|
||||
* for chip and caracter driver interface sides.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
|
||||
* Implementation is based on original LinCAN - Linux CAN bus driver
|
||||
* Part of OrtCAN project https://ortcan.sourceforge.net/
|
||||
* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://control.fel.cvut.cz>
|
||||
* Copyright (C) 2002-2024 Pavel Pisa <pisa@cmp.felk.cvut.cz>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <dev/can/can-impl.h>
|
||||
|
||||
#define RTEMS_CAN_QUEUE_ROUND_ROBIN 1
|
||||
|
||||
int rtems_can_queue_fifo_flush_slots( struct rtems_can_queue_fifo *fifo )
|
||||
{
|
||||
struct rtems_can_queue_slot *slot;
|
||||
int ret = 0;
|
||||
|
||||
rtems_mutex_lock( &fifo->fifo_lock );
|
||||
slot = fifo->head;
|
||||
if ( slot ) {
|
||||
*fifo->tail = fifo->free_list;
|
||||
fifo->free_list = slot;
|
||||
fifo->head = NULL;
|
||||
fifo->tail = &fifo->head;
|
||||
ret |= RTEMS_CAN_FIFOF_INACTIVE;
|
||||
}
|
||||
rtems_can_queue_fifo_clear_flag( fifo, RTEMS_CAN_FIFOF_FULL );
|
||||
rtems_can_queue_fifo_set_flag( fifo, RTEMS_CAN_FIFOF_INACTIVE );
|
||||
if ( fifo->out_taken == 0 ) {
|
||||
if ( rtems_can_queue_fifo_test_and_set_flag( fifo, RTEMS_CAN_FIFOF_EMPTY ) ) {
|
||||
ret |= RTEMS_CAN_FIFOF_EMPTY;
|
||||
}
|
||||
}
|
||||
rtems_mutex_unlock( &fifo->fifo_lock );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rtems_can_queue_fifo_init_slots( struct rtems_can_queue_fifo *fifo )
|
||||
{
|
||||
struct rtems_can_queue_slot *slot;
|
||||
int allocated_slot_count = fifo->allocated_slot_count;
|
||||
|
||||
if ( ( fifo->entry == NULL ) || ( allocated_slot_count == 0 ) ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
slot = fifo->entry;
|
||||
fifo->free_list = slot;
|
||||
while ( --allocated_slot_count ) {
|
||||
slot = slot->next = (void *)slot + rtems_can_queue_fifo_slot_size(
|
||||
fifo->max_data_length
|
||||
);
|
||||
}
|
||||
slot->next = NULL;
|
||||
fifo->head = NULL;
|
||||
fifo->tail = &fifo->head;
|
||||
fifo->out_taken = 0;
|
||||
rtems_can_queue_fifo_set_flag( fifo, RTEMS_CAN_FIFOF_EMPTY );
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void rtems_can_queue_do_edge_decref( struct rtems_can_queue_edge *qedge )
|
||||
{
|
||||
rtems_can_queue_do_edge_decref_body( qedge );
|
||||
}
|
||||
|
||||
int rtems_can_queue_get_inslot(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge **qedgep,
|
||||
struct rtems_can_queue_slot **slotp,
|
||||
int cmd
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_edge *edge;
|
||||
int ret = -2;
|
||||
|
||||
edge = rtems_can_queue_first_inedge( qends );
|
||||
if ( edge ) {
|
||||
if ( rtems_can_queue_fifo_test_flag( &edge->fifo, RTEMS_CAN_FIFOF_BLOCK ) == 0 ) {
|
||||
ret = rtems_can_queue_fifo_get_inslot( &edge->fifo, slotp, cmd );
|
||||
if ( ret == 0 ) {
|
||||
*qedgep = edge;
|
||||
RTEMS_DEBUG_PRINT( "cmd = %d found edge %d\n", cmd, edge->edge_num );
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
rtems_can_queue_edge_decref( edge );
|
||||
}
|
||||
*qedgep = NULL;
|
||||
RTEMS_DEBUG_PRINT( "cmd = %d failed\n", cmd );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rtems_can_queue_get_inslot_for_prio(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge **qedgep,
|
||||
struct rtems_can_queue_slot **slotp,
|
||||
const struct can_frame_header *header,
|
||||
int cmd,
|
||||
int prio
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_edge *edge = NULL;
|
||||
struct rtems_can_queue_edge *bestedge = NULL;
|
||||
int ret = -2;
|
||||
|
||||
rtems_can_queue_for_each_inedge( qends, edge ) {
|
||||
if ( rtems_can_queue_fifo_test_flag( &edge->fifo, RTEMS_CAN_FIFOF_BLOCK ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( header != NULL ) {
|
||||
if ( !rtems_can_queue_filter_match( &edge->filter, header->can_id, header->flags ) ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ( bestedge != NULL ) {
|
||||
if ( bestedge->edge_prio < edge->edge_prio ) {
|
||||
if ( edge->edge_prio > prio ) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if ( bestedge->edge_prio <= prio ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
rtems_can_queue_edge_decref( bestedge );
|
||||
}
|
||||
bestedge = edge;
|
||||
rtems_can_queue_edge_incref( bestedge );
|
||||
}
|
||||
if ( ( edge = bestedge ) != NULL ) {
|
||||
ret = rtems_can_queue_fifo_get_inslot( &edge->fifo, slotp, cmd );
|
||||
if ( ret == 0 ) {
|
||||
*qedgep = edge;
|
||||
RTEMS_DEBUG_PRINT( "cmd = %d prio = %d found edge %d\n", cmd, prio,
|
||||
edge->edge_num );
|
||||
return ret;
|
||||
}
|
||||
rtems_can_queue_edge_decref( bestedge );
|
||||
}
|
||||
*qedgep = NULL;
|
||||
RTEMS_DEBUG_PRINT( "cmd=%d prio=%d failed\n", cmd, prio );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rtems_can_queue_test_inslot( struct rtems_can_queue_ends *qends )
|
||||
{
|
||||
struct rtems_can_queue_edge *edge;
|
||||
int ret = -1;
|
||||
|
||||
edge = rtems_can_queue_first_inedge( qends );
|
||||
if ( edge ) {
|
||||
if ( rtems_can_queue_fifo_test_flag( &edge->fifo, RTEMS_CAN_FIFOF_BLOCK ) == 0 ) {
|
||||
if ( rtems_can_queue_fifo_test_flag( &edge->fifo, RTEMS_CAN_FIFOF_FULL ) == 0 ) {
|
||||
/* There is empty space */
|
||||
rtems_can_queue_edge_decref( edge );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rtems_can_queue_put_inslot(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge *qedge,
|
||||
struct rtems_can_queue_slot *slot
|
||||
)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = rtems_can_queue_fifo_put_inslot( &qedge->fifo, slot );
|
||||
if ( ret ) {
|
||||
rtems_can_queue_activate_edge( qends, qedge );
|
||||
rtems_can_queue_notify_output_ends( qedge, RTEMS_CAN_QUEUE_NOTIFY_PROC );
|
||||
}
|
||||
|
||||
rtems_can_queue_edge_decref( qedge );
|
||||
RTEMS_DEBUG_PRINT( "For edge %d returned %d\n", qedge->edge_num, ret );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rtems_can_queue_abort_inslot(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge *qedge,
|
||||
struct rtems_can_queue_slot *slot
|
||||
)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = rtems_can_queue_fifo_abort_inslot( &qedge->fifo, slot );
|
||||
if ( ret ) {
|
||||
rtems_can_queue_notify_output_ends( qedge, RTEMS_CAN_QUEUE_NOTIFY_SPACE );
|
||||
}
|
||||
|
||||
rtems_can_queue_edge_decref( qedge );
|
||||
RTEMS_DEBUG_PRINT( "For edge %d returned %d\n", qedge->edge_num, ret );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rtems_can_queue_filter_frame_to_edges(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge *src_edge,
|
||||
struct can_frame *frame,
|
||||
unsigned int flags2add
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_edge *edge;
|
||||
struct rtems_can_queue_slot *slot;
|
||||
int destnr = 0;
|
||||
int is_txerr = 0;
|
||||
int ret;
|
||||
|
||||
RTEMS_DEBUG_PRINT( "For frame ID 0x%08lx and flags 0x%02x\n",
|
||||
frame->header.can_id, frame->header.flags );
|
||||
|
||||
is_txerr = ( ( flags2add & CAN_FRAME_TXERR ) == 0 ) ? 0 : 1;
|
||||
|
||||
rtems_can_queue_for_each_inedge( qends, edge ) {
|
||||
if ( rtems_can_queue_fifo_test_flag( &edge->fifo, RTEMS_CAN_FIFOF_BLOCK ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
src_edge != NULL &&
|
||||
is_txerr == 0 &&
|
||||
edge->output_ends == src_edge->input_ends
|
||||
) {
|
||||
flags2add |= CAN_FRAME_ECHO;
|
||||
} else {
|
||||
flags2add &= ~CAN_FRAME_ECHO;
|
||||
}
|
||||
|
||||
if ( !rtems_can_queue_filter_match( &edge->filter, frame->header.can_id,
|
||||
frame->header.flags | flags2add ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = rtems_can_queue_fifo_get_inslot( &edge->fifo, &slot, 0 );
|
||||
if ( ret == 0 ) {
|
||||
if ( frame->header.dlen > edge->fifo.max_data_length ) {
|
||||
rtems_can_queue_fifo_abort_inslot( &edge->fifo, slot );
|
||||
continue;
|
||||
}
|
||||
|
||||
slot->frame = *frame;
|
||||
slot->frame.header.flags |= flags2add;
|
||||
if ( slot->frame.header.flags & CAN_FRAME_ERR ) {
|
||||
/* Invalidate CAN identifier if this is an error frame. This way
|
||||
* the user will know this is not a regular frame even without
|
||||
* flag check.
|
||||
*/
|
||||
slot->frame.header.can_id |= CAN_ERR_ID_TAG;
|
||||
}
|
||||
|
||||
destnr++;
|
||||
ret = rtems_can_queue_fifo_put_inslot( &edge->fifo, slot );
|
||||
if ( ret ) {
|
||||
rtems_can_queue_activate_edge( qends, edge );
|
||||
rtems_can_queue_notify_output_ends(
|
||||
edge,
|
||||
RTEMS_CAN_QUEUE_NOTIFY_PROC
|
||||
);
|
||||
}
|
||||
|
||||
if ( is_txerr == 1 ) {
|
||||
/* Send TX error frame to just one edge and skip all others. */
|
||||
rtems_can_queue_edge_decref( edge );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
RTEMS_DEBUG_PRINT( "Sent frame ID %d to %d edges\n", frame->header.can_id,
|
||||
destnr );
|
||||
|
||||
return destnr;
|
||||
}
|
||||
|
||||
int rtems_can_queue_test_outslot(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge **qedgep,
|
||||
struct rtems_can_queue_slot **slotp
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_edge *edge;
|
||||
int prio = RTEMS_CAN_QUEUE_PRIO_NR - 1;
|
||||
int ret;
|
||||
|
||||
do {
|
||||
rtems_mutex_lock( &qends->ends_lock );
|
||||
while ( !TAILQ_EMPTY( &qends->active[prio] ) ) {
|
||||
edge = TAILQ_FIRST( &qends->active[prio] );
|
||||
if ( !rtems_can_queue_fifo_test_flag( &edge->fifo, RTEMS_CAN_FIFOF_DEAD ) ) {
|
||||
int out_ready;
|
||||
rtems_can_queue_edge_incref( edge );
|
||||
rtems_mutex_unlock( &qends->ends_lock );
|
||||
*qedgep = edge;
|
||||
RTEMS_DEBUG_PRINT( "Found edge %d\n", edge->edge_num );
|
||||
ret = rtems_can_queue_fifo_test_outslot( &edge->fifo, slotp );
|
||||
if ( ( ret >= 0 ) && !RTEMS_CAN_QUEUE_ROUND_ROBIN ) {
|
||||
return ret;
|
||||
}
|
||||
rtems_mutex_lock( &qends->ends_lock );
|
||||
rtems_mutex_lock( &edge->fifo.fifo_lock );
|
||||
out_ready = rtems_can_queue_fifo_out_is_ready_unprotected(
|
||||
&edge->fifo
|
||||
);
|
||||
if ( !out_ready || RTEMS_CAN_QUEUE_ROUND_ROBIN ) {
|
||||
if ( edge->peershead != NULL ) {
|
||||
TAILQ_REMOVE( edge->peershead, edge, activepeers );
|
||||
}
|
||||
if ( out_ready ) {
|
||||
TAILQ_INSERT_HEAD(
|
||||
&qends->active[edge->edge_prio],
|
||||
edge,
|
||||
activepeers
|
||||
);
|
||||
edge->peershead = &qends->active[edge->edge_prio];
|
||||
} else {
|
||||
rtems_can_queue_fifo_set_flag(
|
||||
&edge->fifo,
|
||||
RTEMS_CAN_FIFOF_INACTIVE
|
||||
);
|
||||
TAILQ_INSERT_TAIL( &qends->idle, edge, activepeers );
|
||||
edge->peershead = &qends->idle;
|
||||
}
|
||||
}
|
||||
rtems_mutex_unlock( &edge->fifo.fifo_lock );
|
||||
rtems_mutex_unlock( &qends->ends_lock );
|
||||
if ( ret >= 0 ) {
|
||||
return ret;
|
||||
}
|
||||
rtems_can_queue_edge_decref( edge );
|
||||
rtems_mutex_lock( &qends->ends_lock );
|
||||
} else {
|
||||
rtems_mutex_lock( &edge->fifo.fifo_lock );
|
||||
rtems_can_queue_fifo_set_flag( &edge->fifo, RTEMS_CAN_FIFOF_INACTIVE );
|
||||
rtems_can_queue_edge_to_idle_unprotected( qends, edge );
|
||||
rtems_mutex_unlock( &edge->fifo.fifo_lock );
|
||||
}
|
||||
}
|
||||
rtems_mutex_unlock( &qends->ends_lock );
|
||||
} while ( --prio >= 0 );
|
||||
|
||||
*qedgep = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int rtems_can_queue_pending_outslot_prio(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
int prio_min
|
||||
) {
|
||||
struct rtems_can_queue_edge *edge;
|
||||
int prio;
|
||||
|
||||
for ( prio = RTEMS_CAN_QUEUE_PRIO_NR; --prio >= prio_min; ) {
|
||||
rtems_mutex_lock( &qends->ends_lock );
|
||||
while ( !TAILQ_EMPTY( &qends->active[prio] ) ) {
|
||||
edge = TAILQ_FIRST( &qends->active[prio] );
|
||||
if ( !rtems_can_queue_fifo_test_flag( &edge->fifo, RTEMS_CAN_FIFOF_DEAD ) ) {
|
||||
rtems_can_queue_edge_incref( edge );
|
||||
rtems_mutex_unlock( &qends->ends_lock );
|
||||
if ( rtems_can_queue_fifo_out_is_ready_unprotected( &edge->fifo ) ) {
|
||||
return prio;
|
||||
}
|
||||
rtems_mutex_lock( &qends->ends_lock );
|
||||
rtems_mutex_lock( &edge->fifo.fifo_lock );
|
||||
if ( !rtems_can_queue_fifo_out_is_ready_unprotected( &edge->fifo ) ) {
|
||||
rtems_can_queue_fifo_set_flag(
|
||||
&edge->fifo,
|
||||
RTEMS_CAN_FIFOF_INACTIVE
|
||||
);
|
||||
rtems_can_queue_edge_to_idle_unprotected( qends, edge );
|
||||
}
|
||||
rtems_mutex_unlock( &edge->fifo.fifo_lock );
|
||||
rtems_mutex_unlock( &qends->ends_lock );
|
||||
rtems_can_queue_edge_decref( edge );
|
||||
rtems_mutex_lock( &qends->ends_lock );
|
||||
} else {
|
||||
rtems_mutex_lock( &edge->fifo.fifo_lock );
|
||||
rtems_can_queue_fifo_set_flag( &edge->fifo, RTEMS_CAN_FIFOF_INACTIVE );
|
||||
rtems_can_queue_edge_to_idle_unprotected( qends, edge );
|
||||
rtems_mutex_unlock( &edge->fifo.fifo_lock );
|
||||
}
|
||||
}
|
||||
rtems_mutex_unlock( &qends->ends_lock );
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int rtems_can_queue_free_outslot(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge *qedge,
|
||||
struct rtems_can_queue_slot *slot
|
||||
)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = rtems_can_queue_fifo_free_outslot( &qedge->fifo, slot );
|
||||
if ( ret & RTEMS_CAN_FIFOF_EMPTY ) {
|
||||
rtems_can_queue_notify_input_ends( qedge, RTEMS_CAN_QUEUE_NOTIFY_EMPTY );
|
||||
}
|
||||
|
||||
if ( ret & RTEMS_CAN_FIFOF_FULL ) {
|
||||
rtems_can_queue_notify_input_ends( qedge, RTEMS_CAN_QUEUE_NOTIFY_SPACE );
|
||||
}
|
||||
if ( ret & RTEMS_CAN_FIFOF_INACTIVE ) {
|
||||
rtems_mutex_lock( &qends->ends_lock );
|
||||
rtems_mutex_lock( &qedge->fifo.fifo_lock );
|
||||
if ( !rtems_can_queue_fifo_out_is_ready_unprotected( &qedge->fifo ) ) {
|
||||
rtems_can_queue_fifo_set_flag( &qedge->fifo, RTEMS_CAN_FIFOF_INACTIVE );
|
||||
rtems_can_queue_edge_to_idle_unprotected( qends, qedge );
|
||||
}
|
||||
rtems_mutex_unlock( &qedge->fifo.fifo_lock );
|
||||
rtems_mutex_unlock( &qends->ends_lock );
|
||||
}
|
||||
rtems_can_queue_edge_decref( qedge );
|
||||
RTEMS_DEBUG_PRINT( "For edge %d returned %d\n", qedge->edge_num, ret );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rtems_can_queue_push_back_outslot(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge *qedge,
|
||||
struct rtems_can_queue_slot *slot
|
||||
)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = rtems_can_queue_fifo_again_outslot( &qedge->fifo, slot );
|
||||
|
||||
rtems_mutex_lock( &qends->ends_lock );
|
||||
rtems_mutex_lock( &qedge->fifo.fifo_lock );
|
||||
if ( rtems_can_queue_fifo_out_is_ready_unprotected( &qedge->fifo ) ) {
|
||||
if ( qedge->peershead != NULL ) {
|
||||
TAILQ_REMOVE( qedge->peershead, qedge, activepeers );
|
||||
}
|
||||
TAILQ_INSERT_TAIL( &qends->active[qedge->edge_prio], qedge, activepeers );
|
||||
qedge->peershead = &qends->active[qedge->edge_prio];
|
||||
rtems_can_queue_fifo_clear_flag( &qedge->fifo, RTEMS_CAN_FIFOF_INACTIVE );
|
||||
}
|
||||
rtems_mutex_unlock( &qedge->fifo.fifo_lock );
|
||||
rtems_mutex_unlock( &qends->ends_lock );
|
||||
|
||||
rtems_can_queue_edge_decref( qedge );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rtems_can_queue_flush( struct rtems_can_queue_edge *qedge )
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = rtems_can_queue_fifo_flush_slots( &qedge->fifo );
|
||||
if ( ret != 0 ) {
|
||||
rtems_can_queue_notify_input_ends( qedge, RTEMS_CAN_QUEUE_NOTIFY_EMPTY );
|
||||
rtems_can_queue_notify_input_ends( qedge, RTEMS_CAN_QUEUE_NOTIFY_SPACE );
|
||||
rtems_mutex_lock( &qedge->output_ends->ends_lock );
|
||||
rtems_mutex_lock( &qedge->fifo.fifo_lock );
|
||||
if ( !rtems_can_queue_fifo_out_is_ready_unprotected( &qedge->fifo ) &&
|
||||
( qedge->output_ends != NULL ) ) {
|
||||
rtems_can_queue_fifo_set_flag( &qedge->fifo, RTEMS_CAN_FIFOF_INACTIVE );
|
||||
rtems_can_queue_edge_to_idle_unprotected(qedge->output_ends, qedge);
|
||||
}
|
||||
rtems_mutex_unlock( &qedge->fifo.fifo_lock );
|
||||
rtems_mutex_unlock( &qedge->output_ends->ends_lock );
|
||||
}
|
||||
RTEMS_DEBUG_PRINT( "For edge %d returned %d\n", qedge->edge_num, ret );
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rtems_can_queue_ends_init( struct rtems_can_queue_ends *qends )
|
||||
{
|
||||
int i;
|
||||
|
||||
for ( i = RTEMS_CAN_QUEUE_PRIO_NR; --i >= 0; ) {
|
||||
TAILQ_INIT( &qends->active[i] );
|
||||
}
|
||||
TAILQ_INIT( &qends->idle );
|
||||
TAILQ_INIT( &qends->inlist );
|
||||
TAILQ_INIT( &qends->outlist );
|
||||
rtems_mutex_init( &qends->ends_lock, "ends_lock" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtems_can_queue_connect_edge(
|
||||
struct rtems_can_queue_edge *qedge,
|
||||
struct rtems_can_queue_ends *input_ends,
|
||||
struct rtems_can_queue_ends *output_ends
|
||||
)
|
||||
{
|
||||
if ( qedge == NULL ) {
|
||||
return -1;
|
||||
}
|
||||
RTEMS_DEBUG_PRINT( "Connecting edge %d\n", qedge->edge_num );
|
||||
rtems_can_queue_edge_incref( qedge );
|
||||
|
||||
if ( input_ends < output_ends ) {
|
||||
rtems_mutex_lock( &input_ends->ends_lock );
|
||||
rtems_mutex_lock( &output_ends->ends_lock );
|
||||
} else {
|
||||
rtems_mutex_lock( &output_ends->ends_lock );
|
||||
if ( output_ends != input_ends ) {
|
||||
rtems_mutex_lock( &input_ends->ends_lock );
|
||||
}
|
||||
}
|
||||
|
||||
rtems_mutex_lock( &qedge->fifo.fifo_lock );
|
||||
qedge->input_ends=input_ends;
|
||||
TAILQ_INSERT_TAIL( &input_ends->inlist, qedge, input_peers );
|
||||
qedge->output_ends=output_ends;
|
||||
TAILQ_INSERT_TAIL( &output_ends->outlist, qedge, output_peers );
|
||||
TAILQ_INSERT_TAIL( &output_ends->idle, qedge, activepeers );
|
||||
qedge->peershead = &output_ends->idle;
|
||||
rtems_mutex_unlock( &qedge->fifo.fifo_lock );
|
||||
|
||||
if ( input_ends < output_ends ) {
|
||||
rtems_mutex_unlock( &output_ends->ends_lock );
|
||||
rtems_mutex_unlock( &input_ends->ends_lock );
|
||||
} else {
|
||||
if ( output_ends != input_ends ) {
|
||||
rtems_mutex_unlock( &input_ends->ends_lock );
|
||||
}
|
||||
rtems_mutex_unlock( &output_ends->ends_lock );
|
||||
}
|
||||
|
||||
rtems_can_queue_notify_both_ends( qedge, RTEMS_CAN_QUEUE_NOTIFY_ATTACH );
|
||||
|
||||
if ( rtems_can_queue_fifo_test_and_set_flag(
|
||||
&qedge->fifo, RTEMS_CAN_FIFOF_READY
|
||||
) ) {
|
||||
rtems_can_queue_edge_decref( qedge );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtems_can_queue_disconnect_edge( struct rtems_can_queue_edge *qedge )
|
||||
{
|
||||
int ret;
|
||||
struct rtems_can_queue_ends *input_ends, *output_ends;
|
||||
|
||||
input_ends=qedge->input_ends;
|
||||
output_ends=qedge->output_ends;
|
||||
|
||||
if ( input_ends && output_ends ) {
|
||||
if ( input_ends < output_ends ) {
|
||||
rtems_mutex_lock( &input_ends->ends_lock );
|
||||
rtems_mutex_lock( &output_ends->ends_lock );
|
||||
} else {
|
||||
rtems_mutex_lock( &output_ends->ends_lock );
|
||||
if ( output_ends != input_ends ) {
|
||||
rtems_mutex_lock( &input_ends->ends_lock );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RTEMS_DEBUG_PRINT( "Called with not fully connected edge\n" );
|
||||
if ( input_ends ) {
|
||||
rtems_mutex_lock( &input_ends->ends_lock );
|
||||
} else if ( output_ends ) {
|
||||
rtems_mutex_lock( &output_ends->ends_lock );
|
||||
}
|
||||
}
|
||||
|
||||
rtems_mutex_lock( &qedge->fifo.fifo_lock );
|
||||
if ( atomic_load( &qedge->edge_used ) == 0 ) {
|
||||
if ( qedge->output_ends ) {
|
||||
if ( qedge->peershead != NULL ) {
|
||||
TAILQ_REMOVE( qedge->peershead, qedge, activepeers );
|
||||
qedge->peershead = NULL;
|
||||
}
|
||||
TAILQ_REMOVE( &qedge->output_ends->outlist, qedge, output_peers );
|
||||
qedge->output_ends=NULL;
|
||||
}
|
||||
if ( qedge->input_ends ) {
|
||||
TAILQ_REMOVE( &qedge->input_ends->inlist, qedge, input_peers );
|
||||
qedge->input_ends=NULL;
|
||||
}
|
||||
ret = 1;
|
||||
} else {
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
rtems_mutex_unlock( &qedge->fifo.fifo_lock );
|
||||
|
||||
if ( input_ends && output_ends ) {
|
||||
if ( input_ends < output_ends ) {
|
||||
rtems_mutex_unlock( &output_ends->ends_lock );
|
||||
rtems_mutex_unlock( &input_ends->ends_lock );
|
||||
} else {
|
||||
if ( output_ends != input_ends ) {
|
||||
rtems_mutex_unlock( &input_ends->ends_lock );
|
||||
}
|
||||
rtems_mutex_unlock( &output_ends->ends_lock );
|
||||
}
|
||||
} else {
|
||||
if ( input_ends ) {
|
||||
rtems_mutex_unlock( &input_ends->ends_lock );
|
||||
} else if ( output_ends ) {
|
||||
rtems_mutex_unlock( &output_ends->ends_lock );
|
||||
}
|
||||
}
|
||||
|
||||
RTEMS_DEBUG_PRINT( "Edge %d returned %d\n", qedge->edge_num, ret );
|
||||
return ret;
|
||||
}
|
||||
|
||||
void rtems_can_queue_block_inlist( struct rtems_can_queue_ends *qends )
|
||||
{
|
||||
struct rtems_can_queue_edge *edge;
|
||||
|
||||
rtems_can_queue_for_each_inedge( qends, edge ) {
|
||||
rtems_can_queue_fifo_set_flag( &edge->fifo, RTEMS_CAN_FIFOF_BLOCK );
|
||||
}
|
||||
}
|
||||
|
||||
void rtems_can_queue_block_outlist( struct rtems_can_queue_ends *qends )
|
||||
{
|
||||
struct rtems_can_queue_edge *edge;
|
||||
|
||||
rtems_can_queue_for_each_outedge( qends, edge ) {
|
||||
rtems_can_queue_fifo_set_flag( &edge->fifo, RTEMS_CAN_FIFOF_BLOCK );
|
||||
}
|
||||
}
|
||||
|
||||
int rtems_can_queue_ends_kill_inlist(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
int send_rest
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_edge *edge;
|
||||
|
||||
rtems_can_queue_for_each_inedge( qends, edge ) {
|
||||
rtems_can_queue_notify_both_ends(
|
||||
edge,
|
||||
RTEMS_CAN_QUEUE_NOTIFY_DEAD_WANTED
|
||||
);
|
||||
if ( send_rest ) {
|
||||
rtems_can_queue_edge_incref( edge );
|
||||
if ( !rtems_can_queue_fifo_test_and_set_flag(
|
||||
&edge->fifo,
|
||||
RTEMS_CAN_FIFOF_FREEONEMPTY
|
||||
) ) {
|
||||
if ( !rtems_can_queue_fifo_test_flag( &edge->fifo, RTEMS_CAN_FIFOF_EMPTY ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( !rtems_can_queue_fifo_test_and_clear_fl(
|
||||
&edge->fifo,
|
||||
RTEMS_CAN_FIFOF_FREEONEMPTY
|
||||
) ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
rtems_can_queue_edge_decref( edge );
|
||||
}
|
||||
}
|
||||
|
||||
return TAILQ_EMPTY( &qends->inlist ) ? 0 : 1;
|
||||
}
|
||||
|
||||
int rtems_can_queue_ends_kill_outlist( struct rtems_can_queue_ends *qends )
|
||||
{
|
||||
struct rtems_can_queue_edge *edge;
|
||||
|
||||
rtems_can_queue_for_each_outedge( qends, edge ) {
|
||||
rtems_can_queue_notify_both_ends(
|
||||
edge,
|
||||
RTEMS_CAN_QUEUE_NOTIFY_DEAD_WANTED
|
||||
);
|
||||
}
|
||||
|
||||
return TAILQ_EMPTY( &qends->outlist ) ? 0 : 1;
|
||||
}
|
||||
|
||||
int rtems_can_queue_ends_flush_inlist( struct rtems_can_queue_ends *qends )
|
||||
{
|
||||
struct rtems_can_queue_edge *edge;
|
||||
|
||||
rtems_can_queue_for_each_inedge( qends, edge ) {
|
||||
rtems_can_queue_flush( edge );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtems_can_queue_ends_flush_outlist( struct rtems_can_queue_ends *qends )
|
||||
{
|
||||
struct rtems_can_queue_edge *edge;
|
||||
|
||||
rtems_can_queue_for_each_outedge( qends, edge ) {
|
||||
rtems_can_queue_flush( edge );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
211
cpukit/include/dev/can/can-bittiming.h
Normal file
211
cpukit/include/dev/can/can-bittiming.h
Normal file
@@ -0,0 +1,211 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup CANFDStack
|
||||
*
|
||||
* @brief This header file is part of CAN/CAN FD bus common support. It defines
|
||||
* structures used for bit timing representation and settings.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
|
||||
* Implementation is based on original LinCAN - Linux CAN bus driver
|
||||
* Part of OrtCAN project https://ortcan.sourceforge.net/
|
||||
* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://control.fel.cvut.cz>
|
||||
* Copyright (C) 2002-2024 Pavel Pisa <pisa@cmp.felk.cvut.cz>
|
||||
* Copyright (C) 2005 Stanislav Marek
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _DEV_CAN_CAN_BITTIMING_H
|
||||
#define _DEV_CAN_CAN_BITTIMING_H
|
||||
|
||||
/**
|
||||
* @name Bit Timing Type
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief This define is used to select nominal bit timing.
|
||||
*/
|
||||
#define RTEMS_CAN_BITTIME_NOMINAL ( 0 )
|
||||
/**
|
||||
* @brief This define is used to select data bit timing.
|
||||
*/
|
||||
#define RTEMS_CAN_BITTIME_DATA ( 1 )
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Bit Timing Source
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief This define is used to use bit timing calculation from bitrate.
|
||||
*/
|
||||
#define RTEMS_CAN_BITTIME_FROM_BITRATE ( 0 )
|
||||
/**
|
||||
* @brief This define is used to use bit timing from precomputed values.
|
||||
*/
|
||||
#define RTEMS_CAN_BITTIME_FROM_PRECOMPUTED ( 1 )
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief This structure is used to represent CAN bit timing constants
|
||||
*
|
||||
* Controller should define these constants based on its maximal and minimal
|
||||
* values.
|
||||
*/
|
||||
struct rtems_can_bittiming_const {
|
||||
/**
|
||||
* @brief This member holds the controller's name.
|
||||
*/
|
||||
const char *name;
|
||||
/**
|
||||
* @brief This member holds mimimal possible TSEG1 value.
|
||||
*/
|
||||
uint32_t tseg1_min;
|
||||
/**
|
||||
* @brief This member holds maximal possible TSEG1 value.
|
||||
*/
|
||||
uint32_t tseg1_max;
|
||||
/**
|
||||
* @brief This member holds mimimal possible TSEG2 value.
|
||||
*/
|
||||
uint32_t tseg2_min;
|
||||
/**
|
||||
* @brief This member holds maximal possible TSEG2 value.
|
||||
*/
|
||||
uint32_t tseg2_max;
|
||||
/**
|
||||
* @brief This member holds maximal possible Sync Jump Width value.
|
||||
*/
|
||||
uint32_t sjw_max;
|
||||
/**
|
||||
* @brief This member holds mimimal possible Bit Rate Prescaler value.
|
||||
*/
|
||||
uint32_t brp_min;
|
||||
/**
|
||||
* @brief This member holds maximal possible Bit Rate Prescaler value.
|
||||
*/
|
||||
uint32_t brp_max;
|
||||
/**
|
||||
* @brief This member holds Bit Rate Prescaler initial value.
|
||||
*/
|
||||
uint32_t brp_inc;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This structure is used to represent CAN bit timing
|
||||
*
|
||||
* This structure is used to set new bit timing or obtain the current one.
|
||||
*/
|
||||
struct rtems_can_bittiming {
|
||||
/**
|
||||
* @brief This member holds CAN bitrate.
|
||||
*/
|
||||
uint32_t bitrate;
|
||||
/**
|
||||
* @brief This member holds CAN sample point.
|
||||
*/
|
||||
uint32_t sample_point;
|
||||
/**
|
||||
* @brief This member holds time quantum value.
|
||||
*/
|
||||
uint32_t tq;
|
||||
/**
|
||||
* @brief This member holds propagation segment.
|
||||
*/
|
||||
uint32_t prop_seg;
|
||||
/**
|
||||
* @brief This member holds phase segment 1.
|
||||
*/
|
||||
uint32_t phase_seg1;
|
||||
/**
|
||||
* @brief This member holds phase segment 2.
|
||||
*/
|
||||
uint32_t phase_seg2;
|
||||
/**
|
||||
* @brief This member holds Sync Jump Width value.
|
||||
*/
|
||||
uint32_t sjw;
|
||||
/**
|
||||
* @brief This member holds Bit Rate Prescaler value.
|
||||
*/
|
||||
uint32_t brp;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This structure is used to set CAN bit timing values via IOCTL call.
|
||||
*
|
||||
*/
|
||||
struct rtems_can_set_bittiming {
|
||||
/**
|
||||
* @brief This member specifies which bittiming is to be set (nominal, data)
|
||||
* @ref RTEMS_CAN_BITTIME_NOMINAL - nominal bit timing
|
||||
* @ref RTEMS_CAN_BITTIME_DATA - data bit timing (for CAN FD chips)
|
||||
*/
|
||||
uint16_t type;
|
||||
/**
|
||||
* @brief This member specifies the source of bit timing constants
|
||||
* @ref RTEMS_CAN_BITTIME_FROM_BITRATE - calculate from bitrate
|
||||
* @ref RTEMS_CAN_BITTIME_FROM_PRECOMPUTED - used precomputed values
|
||||
*/
|
||||
uint16_t from;
|
||||
/**
|
||||
* @brief This member holds the @ref rtems_can_bittiming structure. This
|
||||
* is used to specify bitrate or precomputed values.
|
||||
*/
|
||||
struct rtems_can_bittiming bittiming;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This structure is used to get CAN bit timing values via IOCTL call.
|
||||
*
|
||||
*/
|
||||
struct rtems_can_get_bittiming {
|
||||
/**
|
||||
* @brief This member specifies which bittiming is to be set (nominal, data)
|
||||
* @ref RTEMS_CAN_BITTIME_NOMINAL - nominal bit timing
|
||||
* @ref RTEMS_CAN_BITTIME_DATA - data bit timing (for CAN FD chips)
|
||||
*/
|
||||
uint16_t type;
|
||||
/**
|
||||
* @brief This member holds the rtems_can_bittiming structure. This
|
||||
* represents currently set values.
|
||||
*/
|
||||
struct rtems_can_bittiming bittiming;
|
||||
/**
|
||||
* @brief This member holds the @ref rtems_can_bittiming_const structure.
|
||||
* This represents maximum and minimal possible bit timing values.
|
||||
*/
|
||||
struct rtems_can_bittiming_const bittiming_const;
|
||||
};
|
||||
|
||||
#endif /* _DEV_CAN_CAN_BITTIMING_H */
|
||||
97
cpukit/include/dev/can/can-bus.h
Normal file
97
cpukit/include/dev/can/can-bus.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup CANFDStack
|
||||
*
|
||||
* @brief This header file is part of CAN/CAN FD bus common support. It
|
||||
* declares structures and functions used for CAN bus registration.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
|
||||
* Implementation is based on original LinCAN - Linux CAN bus driver
|
||||
* Part of OrtCAN project https://ortcan.sourceforge.net/
|
||||
* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://control.fel.cvut.cz>
|
||||
* Copyright (C) 2002-2024 Pavel Pisa <pisa@cmp.felk.cvut.cz>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _DEV_CAN_CAN_BUS_H
|
||||
#define _DEV_CAN_CAN_BUS_H
|
||||
|
||||
#include <rtems.h>
|
||||
#include <stdint.h>
|
||||
#include <stdatomic.h>
|
||||
#include <sys/ioccom.h>
|
||||
#include <rtems/thread.h>
|
||||
|
||||
struct rtems_can_chip;
|
||||
|
||||
/**
|
||||
* @brief This structure represents CAN bus device. This is the main
|
||||
* structure passed throught file system during read/write/ioctl/close
|
||||
* operations.
|
||||
*
|
||||
* The structure should be allocated by the user in the application.
|
||||
*/
|
||||
struct rtems_can_bus {
|
||||
/**
|
||||
* @brief This member holds the pointer to @ref rtems_can_chip structure.
|
||||
* Controller specific initialization function should fill this
|
||||
* structure properly.
|
||||
*/
|
||||
struct rtems_can_chip *chip;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This function notifies all users the chip was stopped.
|
||||
*
|
||||
* This should be called from the chip's stop function once all
|
||||
* frames are either aborted or flushed from the FIFOs. It basically
|
||||
* unblocks users waiting on a blocking read.
|
||||
*
|
||||
* @param chip Pointer to @ref rtems_can_chip structure.
|
||||
*
|
||||
* @return Zero on success, negative value on error.
|
||||
*
|
||||
*/
|
||||
int rtems_can_bus_notify_chip_stop( struct rtems_can_chip *chip );
|
||||
|
||||
/**
|
||||
* @brief This function registers CAN device into /dev namespace.
|
||||
*
|
||||
* This should be called with already allocated @ref rtems_can_bus structure
|
||||
* and initialized controller to be registered.
|
||||
*
|
||||
* @param bus Pointer to @ref rtems_can_bus structure.
|
||||
* @param bus_path Entire path to which the device should be registered
|
||||
* (i.e /dev/can0, /dev/my_can etc.).
|
||||
*
|
||||
* @return Zero on success, negative value on error.
|
||||
*
|
||||
*/
|
||||
int rtems_can_bus_register( struct rtems_can_bus *bus, const char *bus_path );
|
||||
|
||||
#endif /* _DEV_CAN_CAN_DEVCOMMON_H */
|
||||
418
cpukit/include/dev/can/can-devcommon.h
Normal file
418
cpukit/include/dev/can/can-devcommon.h
Normal file
@@ -0,0 +1,418 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup CANFDStack
|
||||
*
|
||||
* @brief This header file is part of CAN/CAN FD bus common support. It
|
||||
* declares structures and functions used for controller's side of
|
||||
* FIFO operations.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
|
||||
* Implementation is based on original LinCAN - Linux CAN bus driver
|
||||
* Part of OrtCAN project https://ortcan.sourceforge.net/
|
||||
* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://control.fel.cvut.cz>
|
||||
* Copyright (C) 2002-2024 Pavel Pisa <pisa@cmp.felk.cvut.cz>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _DEV_CAN_CAN_DEVCOMMON_H
|
||||
#define _DEV_CAN_CAN_DEVCOMMON_H
|
||||
|
||||
#include <rtems.h>
|
||||
#include <stdint.h>
|
||||
#include <stdatomic.h>
|
||||
#include <sys/ioccom.h>
|
||||
#include <rtems/thread.h>
|
||||
|
||||
#include <dev/can/can.h>
|
||||
#include <dev/can/can-bus.h>
|
||||
#include <dev/can/can-impl.h>
|
||||
|
||||
/**
|
||||
* @brief This structure holds the controller's (application) side of
|
||||
* queue's ends.
|
||||
*/
|
||||
struct rtems_can_queue_ends_dev {
|
||||
/**
|
||||
* @brief This member holds base @ref rtems_can_queue_ends structure.
|
||||
*/
|
||||
struct rtems_can_queue_ends base;
|
||||
/**
|
||||
* @brief This member holds the chip's structure.
|
||||
*/
|
||||
struct rtems_can_chip *chip;
|
||||
/**
|
||||
* @brief This member holds the worker semaphore used to trigger controller
|
||||
* when there is a new message to be transmitted.
|
||||
*/
|
||||
rtems_binary_semaphore worker_sem;
|
||||
};
|
||||
|
||||
struct rtems_can_user;
|
||||
TAILQ_HEAD( rtems_can_user_list_t, rtems_can_user );
|
||||
|
||||
struct rtems_can_chip;
|
||||
|
||||
/**
|
||||
* @brief This structure represents CAN controller operations. These
|
||||
* provides interface from IOCTL calls to controller functions and should
|
||||
* be registered during controller initialization.
|
||||
*/
|
||||
struct rtems_can_chip_ops {
|
||||
/**
|
||||
* @brief Starts the chip. Called with @ref RTEMS_CAN_CHIP_START ioctl.
|
||||
*
|
||||
* The function should atomically set @ref RTEMS_CAN_CHIP_RUNNING
|
||||
* flag in flags field of @ref rtems_can_chip structure and abort all
|
||||
* currently filled HW buffers.
|
||||
*
|
||||
* @param chip Pointer to chip structure.
|
||||
*
|
||||
* @return 0 on success, negative value otherwise.
|
||||
*/
|
||||
int ( *start_chip )( struct rtems_can_chip *chip );
|
||||
/**
|
||||
* @brief Stops the chip. Called with @ref RTEMS_CAN_CHIP_STOP ioctl.
|
||||
*
|
||||
* The function should atomically clear @ref RTEMS_CAN_CHIP_RUNNING
|
||||
* flag in flags field of @ref rtems_can_chip structure.
|
||||
*
|
||||
* The function is blocking and passed with an option argument that
|
||||
* provides the timeout (not blocking at all if argument is NULL). The
|
||||
* function should abort all outgoing TX frames and clear the FIFO queues,
|
||||
* returning the frames to the user as TX error frames. The flush takes
|
||||
* places if those frames are not returned within the specified timeout.
|
||||
*
|
||||
* @param chip Pointer to @ref rtems_can_chip structure.
|
||||
* @param ts Pointer to the stop operation timeout.
|
||||
*
|
||||
* @return 0 on success, negative value otherwise.
|
||||
*/
|
||||
int ( *stop_chip )( struct rtems_can_chip *chip, struct timespec *ts );
|
||||
/**
|
||||
* @brief Unrecognized ioctl calls are passed to chip specific
|
||||
* function to handle them. This way chip specific ioctl calls can
|
||||
* be implemented.
|
||||
*
|
||||
* @param chip Pointer to chip structure.
|
||||
* @param command IOCTL command
|
||||
* @param arg Void pointer to IOCL argument
|
||||
*
|
||||
* @return 0 on success, negative value otherwise.
|
||||
*/
|
||||
int ( *chip_ioctl )(
|
||||
struct rtems_can_chip *chip,
|
||||
ioctl_command_t command,
|
||||
void *arg
|
||||
);
|
||||
/**
|
||||
* @brief Obtains timestamp from the controller. Called with
|
||||
* @ref RTEMS_CAN_CHIP_GET_TIMESTAMP ioctl.
|
||||
*
|
||||
* @param chip Pointer to chip structure.
|
||||
* @param timestamp Pointer to uint64_t integer where timestamp is stored
|
||||
*
|
||||
* @return 0 on success, negative value otherwise.
|
||||
*/
|
||||
int ( *get_chip_timestamp )(
|
||||
struct rtems_can_chip *chip,
|
||||
uint64_t *timestamp
|
||||
);
|
||||
/**
|
||||
* @brief Obtains controller's information specified by input integer
|
||||
* argument. Called with @ref RTEMS_CAN_CHIP_GET_INFO ioctl.
|
||||
*
|
||||
* @param chip Pointer to chip structure.
|
||||
* @param what Integer specifying what info should be retrieved. Refer to
|
||||
* @ref CANChip info defines.
|
||||
*/
|
||||
int ( *get_chip_info )( struct rtems_can_chip *chip, int what );
|
||||
/**
|
||||
* @brief Calculates bit timing from given bit rate and saves the
|
||||
* values to controller's registers. Called with
|
||||
* @ref RTEMS_CAN_SET_BITRATE ioctl if @ref RTEMS_CAN_BITTIME_FROM_BITRATE
|
||||
* is set.
|
||||
*
|
||||
* @param chip Pointer to chip structure.
|
||||
* @param type Bittiming type (nominal, FD)
|
||||
* @param bt Pointer to rtems_can_bittiming structure
|
||||
*
|
||||
* @return 0 on success, negated errno on error.
|
||||
* @retval -EPERM Chip is already started, cannot change bittiming.
|
||||
* @retval -EINVAL Incorrect bit time type.
|
||||
*/
|
||||
int ( *calc_bittiming )(
|
||||
struct rtems_can_chip *chip,
|
||||
int type,
|
||||
struct rtems_can_bittiming *bt
|
||||
);
|
||||
/**
|
||||
* @brief Checks bit timing given by user and saves it to controller's
|
||||
* registers. Called with @ref RTEMS_CAN_SET_BITRATE ioctl if
|
||||
* @ref RTEMS_CAN_BITTIME_FROM_PRECOMPUTED is set.
|
||||
*
|
||||
* @param chip Pointer to chip structure.
|
||||
* @param type Bittiming type (nominal, FD)
|
||||
* @param bt Pointer to rtems_can_bittiming structure
|
||||
*
|
||||
* @return 0 on success, negated errno on error.
|
||||
* @retval -EPERM Chip is already started, cannot change bittiming.
|
||||
* @retval -EINVAL Incorrect bit time type.
|
||||
*/
|
||||
int ( *check_and_set_bittiming )(
|
||||
struct rtems_can_chip *chip,
|
||||
int type,
|
||||
struct rtems_can_bittiming *bt
|
||||
);
|
||||
/**
|
||||
* @brief Sets controller mode defined in input argument. Called
|
||||
* with @ref RTEMS_CAN_CHIP_SET_MODE.
|
||||
* @param chip Pointer to chip structure.
|
||||
* @param mode Modes to be set.
|
||||
*
|
||||
* @return 0 on success, negated errno on error.
|
||||
*/
|
||||
int ( *set_chip_mode )( struct rtems_can_chip *chip, uint32_t mode );
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This structure represents one CAN controller.
|
||||
*/
|
||||
struct rtems_can_chip {
|
||||
/**
|
||||
* @brief This member holds controller's type/name.
|
||||
*/
|
||||
const char *type;
|
||||
/**
|
||||
* @brief This member holds controller's interrupt number.
|
||||
*/
|
||||
rtems_vector_number irq;
|
||||
/**
|
||||
* @brief This member holds controller's base clock frequency.
|
||||
*/
|
||||
uint32_t freq;
|
||||
/**
|
||||
* @brief This member informs whether close operation is blocking or
|
||||
* nonblocking. This can be set with @ref RTEMS_CAN_CLOSE_NONBLOCK ioctl.
|
||||
*/
|
||||
bool close_nonblock;
|
||||
/**
|
||||
* @brief This member holds controller's flags.
|
||||
*/
|
||||
atomic_uint flags;
|
||||
/**
|
||||
* @brief This member holds the number of users using the controller.
|
||||
*/
|
||||
atomic_uint used;
|
||||
/**
|
||||
* @brief This member holds the currently set mode. Controller's device
|
||||
* driver may set initial modes if needed.
|
||||
*/
|
||||
uint32_t ctrlmode;
|
||||
/**
|
||||
* @brief This member holds modes supported by the controller. Controller's
|
||||
* device driver should set this field. Writing
|
||||
* unsuported mode via @ref RTEMS_CAN_CHIP_SET_MODE ioctl results in error.
|
||||
*/
|
||||
uint32_t ctrlmode_supported;
|
||||
/**
|
||||
* @brief This member holds controller's capabilities.
|
||||
*/
|
||||
uint32_t capabilities;
|
||||
/**
|
||||
* @brief This member holds the lock to ensure atomicity of chip operations.
|
||||
*/
|
||||
rtems_mutex lock;
|
||||
/**
|
||||
* @brief This member is used by the worker to notify the closed operation
|
||||
* is finished.
|
||||
*/
|
||||
rtems_binary_semaphore stop_sem;
|
||||
/**
|
||||
* @brief This member holds nominal bit timing constants (max/min values)
|
||||
*/
|
||||
const struct rtems_can_bittiming_const *bittiming_const;
|
||||
/**
|
||||
* @brief This member holds data bit timing constants (max/min values)
|
||||
*/
|
||||
const struct rtems_can_bittiming_const *data_bittiming_const;
|
||||
/**
|
||||
* @brief This member holds currently set nominal btt timing values.
|
||||
*/
|
||||
struct rtems_can_bittiming bittiming;
|
||||
/**
|
||||
* @brief This member holds currently set data btt timing values.
|
||||
*/
|
||||
struct rtems_can_bittiming data_bittiming;
|
||||
/**
|
||||
* @brief This member holds chip operations.
|
||||
*/
|
||||
struct rtems_can_chip_ops chip_ops;
|
||||
/**
|
||||
* @brief This member holds chip's side of queue ends.
|
||||
*/
|
||||
struct rtems_can_queue_ends_dev *qends_dev;
|
||||
/**
|
||||
* @brief This member holds the list of chip's users.
|
||||
*/
|
||||
struct rtems_can_user_list_t can_users;
|
||||
/**
|
||||
* @brief This member holds the chip's statistics.
|
||||
*/
|
||||
struct rtems_can_stats chip_stats;
|
||||
/**
|
||||
* @brief This member holds chip's specific private structure. This
|
||||
* structure defines non generic fields and setting.
|
||||
*/
|
||||
void *internal;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This structure holds the user's (application) side of queue's ends.
|
||||
*/
|
||||
struct rtems_can_queue_ends_user_t {
|
||||
/**
|
||||
* @brief This member holds base @ref rtems_can_queue_ends structure.
|
||||
*/
|
||||
struct rtems_can_queue_ends base;
|
||||
/**
|
||||
* @brief This member holds semaphore informing the user's side there
|
||||
* is a new message to be read.
|
||||
*/
|
||||
rtems_binary_semaphore sem_read;
|
||||
/**
|
||||
* @brief This member holds semaphore informing the user's side there
|
||||
* is a free space to write message.
|
||||
*/
|
||||
rtems_binary_semaphore sem_write;
|
||||
/**
|
||||
* @brief This member holds semaphore synchronizing queues during close
|
||||
* operation. It informs all messages were sent from the queue.
|
||||
*/
|
||||
rtems_binary_semaphore sem_sync;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This structure represents one CAN user (application).
|
||||
*/
|
||||
struct rtems_can_user {
|
||||
/**
|
||||
* @brief This member holds user's flags.
|
||||
*/
|
||||
unsigned long flags;
|
||||
/**
|
||||
* @brief This member holds TAILQ entry.
|
||||
*/
|
||||
TAILQ_ENTRY( rtems_can_user ) peers;
|
||||
/**
|
||||
* @brief This member holds user's side of queue's ends.
|
||||
*/
|
||||
struct rtems_can_queue_ends_user_t *qends_user;
|
||||
/**
|
||||
* @brief This member holds pointer to @ref rtems_can_bus structure.
|
||||
*/
|
||||
struct rtems_can_bus *bus;
|
||||
/**
|
||||
* @brief This member holds user magic value. It should be set
|
||||
* to @ref RTEMS_CAN_USER_MAGIC value.
|
||||
*/
|
||||
int magic;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This function starts the controller
|
||||
*
|
||||
* Provides common interface to start controller based
|
||||
* on \ref rtems_can_chip structure passed as input parameter.
|
||||
*
|
||||
* @param chip Pointer to \ref rtems_can_chip structure
|
||||
*
|
||||
* @return Zero on success, negative value on error.
|
||||
*
|
||||
*/
|
||||
int rtems_can_chip_start( struct rtems_can_chip *chip );
|
||||
|
||||
/**
|
||||
* @brief This function calculates CAN bit timing for given bit rate.
|
||||
*
|
||||
* This calculates bit timing values for given bit rate (provided in bt
|
||||
* input argument) and controller's maximum/minimal values given by btc
|
||||
* argument.
|
||||
*
|
||||
* @param chip Pointer to @ref rtems_can_chip structure.
|
||||
* @param bt Pointer to @ref rtems_can_bittiming structure. Calculated
|
||||
* values are stored here.
|
||||
* @param btc Pointer to @ref rtems_can_bittiming_const structure. This
|
||||
* provides maximum and minimal bit timing values of the
|
||||
* controller.
|
||||
*
|
||||
* @return Zero on success (and filled bt argument), negative value on error.
|
||||
*
|
||||
*/
|
||||
int rtems_can_bitrate2bittiming(
|
||||
struct rtems_can_chip *chip,
|
||||
struct rtems_can_bittiming *bt,
|
||||
const struct rtems_can_bittiming_const *btc
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief This function fills timestamping with current monotonic time
|
||||
*
|
||||
* @return 64-bit timestamp value.
|
||||
*
|
||||
*/
|
||||
uint64_t rtems_can_fill_timestamp( void );
|
||||
|
||||
/**
|
||||
* @brief Userspace clients specific ends initialization.
|
||||
*
|
||||
* @param qends_user Pointer to @ref rtems_can_queue_ends_user_t structure.
|
||||
*
|
||||
* @return Zero on success; -1 otherwise.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_ends_init_user(
|
||||
struct rtems_can_queue_ends_user_t *qends
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief This function initializes ends from chip's side
|
||||
*
|
||||
* It should be called by CAN controller to initialize its ends.
|
||||
*
|
||||
* @param chip Pointer to @ref rtems_can_chip structure
|
||||
* @param name Name of the controller's worker semaphore
|
||||
*
|
||||
* @return Zero on success, negative value on error.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_ends_init_chip(
|
||||
struct rtems_can_chip *chip,
|
||||
const char *name
|
||||
);
|
||||
|
||||
#endif /* _DEV_CAN_CAN_DEVCOMMON_H */
|
||||
78
cpukit/include/dev/can/can-filter.h
Normal file
78
cpukit/include/dev/can/can-filter.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup CANFDStack
|
||||
*
|
||||
* @brief This header file is part of CAN/CAN FD bus common support. It
|
||||
* implements structure that represents filter for CAN frames.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
|
||||
* Implementation is based on original LinCAN - Linux CAN bus driver
|
||||
* Part of OrtCAN project https://ortcan.sourceforge.net/
|
||||
* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://control.fel.cvut.cz>
|
||||
* Copyright (C) 2002-2024 Pavel Pisa <pisa@cmp.felk.cvut.cz>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _DEV_CAN_CAN_FILTER_H
|
||||
#define _DEV_CAN_CAN_FILTER_H
|
||||
|
||||
/**
|
||||
* @brief This structure is used to represent CAN ID and flags in one
|
||||
* unified structure.
|
||||
*
|
||||
*/
|
||||
struct rtems_can_filter {
|
||||
/**
|
||||
* @brief This member is a bitfield that holds required CAN identifier
|
||||
* values to assign CAN frame to the FIFO queue.
|
||||
*/
|
||||
uint32_t id;
|
||||
/**
|
||||
* @brief This member is a bitfield that holds forbidden CAN identifier
|
||||
* values. If the frame has identifier in range of this bit field,
|
||||
* it is not assigned to the FIFO queue.
|
||||
*/
|
||||
uint32_t id_mask;
|
||||
/**
|
||||
* @brief This member is a bitfield that holds CAN flags required in
|
||||
* CAN frame to be assigned to the FIFO queue.
|
||||
*
|
||||
* Refer to @ref CANFrameFlags for possible flags.
|
||||
*/
|
||||
uint32_t flags;
|
||||
/**
|
||||
* @brief This member is a bitfield that holds CAN flags forbidden in
|
||||
* CAN frame. If the frame has some of these flags, it is not assigned
|
||||
* to the FIFO queue.
|
||||
*
|
||||
* Refer to @ref CANFrameFlags for possible flags.
|
||||
*/
|
||||
uint32_t flags_mask;
|
||||
};
|
||||
|
||||
#endif /* _DEV_CAN_CAN_FILTER_H */
|
||||
559
cpukit/include/dev/can/can-frame.h
Normal file
559
cpukit/include/dev/can/can-frame.h
Normal file
@@ -0,0 +1,559 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup CANFDStack
|
||||
*
|
||||
* @brief This header file is part of CAN/CAN FD bus common support. It
|
||||
* implements CAN frame structure and related defines.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
|
||||
* Implementation is based on original LinCAN - Linux CAN bus driver
|
||||
* Part of OrtCAN project https://ortcan.sourceforge.net/
|
||||
* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://control.fel.cvut.cz>
|
||||
* Copyright (C) 2002-2024 Pavel Pisa <pisa@cmp.felk.cvut.cz>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _DEV_CAN_CAN_FRAME_H
|
||||
#define _DEV_CAN_CAN_FRAME_H
|
||||
|
||||
/**
|
||||
* @defgroup CANFrame
|
||||
*
|
||||
* @ingroup CANFDStack
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief This define provides mask for base frame format identifier.
|
||||
*/
|
||||
#define CAN_FRAME_BFF_ID_MASK ( 0x000007ff )
|
||||
/**
|
||||
* @brief This define provides mask for extended frame format identifier.
|
||||
*/
|
||||
#define CAN_FRAME_EFF_ID_MASK ( 0x1fffffff )
|
||||
/**
|
||||
* @brief Represents standard (not FD) CAN frame data length.
|
||||
*/
|
||||
#define CAN_FRAME_STANDARD_DLEN ( 8u )
|
||||
/**
|
||||
* @brief Represents CAN FD frame data length.
|
||||
*/
|
||||
#define CAN_FRAME_FD_DLEN ( 64u )
|
||||
/**
|
||||
* @brief Represents maximum allowed CAN frame data length.
|
||||
*/
|
||||
#define CAN_FRAME_MAX_DLEN ( CAN_FRAME_FD_DLEN )
|
||||
|
||||
/**
|
||||
* @defgroup CANFrameFlags
|
||||
*
|
||||
* @ingroup CANFrame
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name CAN Frame Flags
|
||||
*
|
||||
* See structure @ref can_frame for reference.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Represents extended frame format.
|
||||
*/
|
||||
#define CAN_FRAME_IDE ( 1 << 0 )
|
||||
/**
|
||||
* @brief Represents remote transfer request.
|
||||
*/
|
||||
#define CAN_FRAME_RTR ( 1 << 1 )
|
||||
/**
|
||||
* @brief Represents echo flag.
|
||||
*/
|
||||
#define CAN_FRAME_ECHO ( 1 << 2 )
|
||||
/**
|
||||
* @brief Represents local flag.
|
||||
*/
|
||||
#define CAN_FRAME_LOCAL ( 1 << 3 )
|
||||
/**
|
||||
* @brief Represents frame transmission error.
|
||||
*/
|
||||
#define CAN_FRAME_TXERR ( 1 << 4 )
|
||||
/**
|
||||
* @brief Represents generic error flag.
|
||||
*/
|
||||
#define CAN_FRAME_ERR ( 1 << 5 )
|
||||
/**
|
||||
* @brief Represents local FIFO overflow.
|
||||
*/
|
||||
#define CAN_FRAME_FIFO_OVERFLOW ( 1 << 6 )
|
||||
/**
|
||||
* @brief Represents CAN FD frame format.
|
||||
*/
|
||||
#define CAN_FRAME_FDF ( 1 << 7 )
|
||||
/**
|
||||
* @brief Represents bit rate shift for CAN FD frames.
|
||||
*/
|
||||
#define CAN_FRAME_BRS ( 1 << 8 )
|
||||
/**
|
||||
* @brief Represents error state inicator of transmitting node.
|
||||
*/
|
||||
#define CAN_FRAME_ESI ( 1 << 9 )
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup CANFrameErrors
|
||||
*
|
||||
* @ingroup CANFrame
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name CAN Error Types
|
||||
*
|
||||
* These types are defined in identifier field of @ref can_frame structure.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Represents CAN frame TX timeout error.
|
||||
*/
|
||||
#define CAN_ERR_ID_TXTIMEOUT ( 1 << 0 )
|
||||
/**
|
||||
* @brief Represents lost arbitration error. Additional information
|
||||
* are stored in @ref CAN_ERR_DATA_BYTE_TRX_CTRL of data.
|
||||
*/
|
||||
#define CAN_ERR_ID_LOSTARB ( 1 << 1 )
|
||||
/**
|
||||
* @brief Represents CAN controller error. Additional information
|
||||
* are stored in @ref CAN_ERR_DATA_BYTE_TRX_PROT of data.
|
||||
*/
|
||||
#define CAN_ERR_ID_CRTL ( 1 << 2 )
|
||||
/**
|
||||
* @brief Represents CAN frame protocol violations. Additional information
|
||||
* are stored in @ref CAN_ERR_DATA_BYTE_TRX_PROT and
|
||||
* @ref CAN_ERR_DATA_BYTE_TRX_PROT_LOC of data.
|
||||
*/
|
||||
#define CAN_ERR_ID_PROT ( 1 << 3 )
|
||||
/**
|
||||
* @brief Represents transceiver status error. Additional information
|
||||
* are stored in @ref CAN_ERR_TRX_DATA_BYTE of data.
|
||||
*/
|
||||
#define CAN_ERR_ID_TRX ( 1 << 4 )
|
||||
/**
|
||||
* @brief Represents no acknowledgment on transmission error.
|
||||
*/
|
||||
#define CAN_ERR_ID_ACK ( 1 << 5 )
|
||||
/**
|
||||
* @brief Represents bus off state.
|
||||
*/
|
||||
#define CAN_ERR_ID_BUSOFF ( 1 << 6 )
|
||||
/**
|
||||
* @brief Represents bus error.
|
||||
*/
|
||||
#define CAN_ERR_ID_BUSERROR ( 1 << 7 )
|
||||
/**
|
||||
* @brief Represents controller restarted information.
|
||||
*/
|
||||
#define CAN_ERR_ID_RESTARTED ( 1 << 8 )
|
||||
/**
|
||||
* @brief Represents TX/RX error counter. Values are stored in
|
||||
* @ref CAN_ERR_DATA_BYTE_CNT_TX and @ref CAN_ERR_DATA_BYTE_CNT_RX of data.
|
||||
*/
|
||||
#define CAN_ERR_ID_CNT ( 1 << 9 )
|
||||
/**
|
||||
* @brief Represents stack internal error.
|
||||
*/
|
||||
#define CAN_ERR_ID_INTERNAL ( 1 << 10 )
|
||||
/**
|
||||
* @brief Represents CAN error tag. This sets 31st bit of identifier to
|
||||
* logical one to make the frame invalid. It should be used to further
|
||||
* distinquish error frames from standard ones.
|
||||
*/
|
||||
#define CAN_ERR_ID_TAG ( 1 << 31 )
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CAN Error Types Offsets to Data
|
||||
*
|
||||
* These defines offsets to data field of @ref can_frame structure, where
|
||||
* additional information for given error type are stored.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Represents data offset in bytes to lost arbitration information.
|
||||
*/
|
||||
#define CAN_ERR_DATA_BYTE_TRX_LOSTARB ( 0 )
|
||||
/**
|
||||
* @brief Represents data offset in bytes to controller error information.
|
||||
*/
|
||||
#define CAN_ERR_DATA_BYTE_TRX_CTRL ( 1 )
|
||||
/**
|
||||
* @brief Represents data offset in bytes to information about protocol
|
||||
* violation.
|
||||
*/
|
||||
#define CAN_ERR_DATA_BYTE_TRX_PROT ( 2 )
|
||||
/**
|
||||
* @brief Represents data offset in bytes to information about protocol
|
||||
* violation location.
|
||||
*/
|
||||
#define CAN_ERR_DATA_BYTE_TRX_PROT_LOC ( 3 )
|
||||
/**
|
||||
* @brief Represents data offset in bytes to additional transceiver status
|
||||
* error information.
|
||||
*/
|
||||
#define CAN_ERR_TRX_DATA_BYTE ( 4 )
|
||||
/**
|
||||
* @brief Represents data offset in bytes to TX counter.
|
||||
*/
|
||||
#define CAN_ERR_DATA_BYTE_CNT_TX ( 6 )
|
||||
/**
|
||||
* @brief Represents data offset in bytes to RX counter.
|
||||
*/
|
||||
#define CAN_ERR_DATA_BYTE_CNT_RX ( 7 )
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CAN Lost Arbitration Errors
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Represents unspecified lost arbitration error.
|
||||
*/
|
||||
#define CAN_ERR_LOSTARB_UNSPEC ( 0 )
|
||||
/**
|
||||
* @brief Represents bit in which arbitration was lost.
|
||||
*/
|
||||
#define CAN_ERR_LOSTARB_BIT( n ) ( n )
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CAN Controller Errors
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Represents unspecified controller error.
|
||||
*/
|
||||
#define CAN_ERR_CRTL_UNSPEC ( 0 )
|
||||
/**
|
||||
* @brief Represents RX buffer overflow controller error.
|
||||
*/
|
||||
#define CAN_ERR_CRTL_RX_OVERFLOW ( 1 << 0 )
|
||||
/**
|
||||
* @brief Represents TX buffer overflow controller error.
|
||||
*/
|
||||
#define CAN_ERR_CRTL_TX_OVERFLOW ( 1 << 1 )
|
||||
/**
|
||||
* @brief Identifies controller reached warning level for RX errors.
|
||||
*/
|
||||
#define CAN_ERR_CRTL_RX_WARNING ( 1 << 2 )
|
||||
/**
|
||||
* @brief Identifies controller reached warning level for TX errors.
|
||||
*/
|
||||
#define CAN_ERR_CRTL_TX_WARNING ( 1 << 3 )
|
||||
/**
|
||||
* @brief Identifies controller reached error passive status for RX.
|
||||
*/
|
||||
#define CAN_ERR_CRTL_RX_PASSIVE ( 1 << 4 )
|
||||
/**
|
||||
* @brief Identifies controller reached error passive status for TX.
|
||||
*/
|
||||
#define CAN_ERR_CRTL_TX_PASSIVE ( 1 << 5 )
|
||||
/**
|
||||
* @brief Identifies controller recovered to error active state.
|
||||
*/
|
||||
#define CAN_ERR_CRTL_ACTIVE ( 1 << 6 )
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CAN Protocol Violation Errors
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Represents unspecified protocol violation.
|
||||
*/
|
||||
#define CAN_ERR_PROT_UNSPEC ( 0 )
|
||||
/**
|
||||
* @brief Represents single bit error.
|
||||
*/
|
||||
#define CAN_ERR_PROT_BIT (1 << 0)
|
||||
/**
|
||||
* @brief Represents frame format error.
|
||||
*/
|
||||
#define CAN_ERR_PROT_FOR (1 << 1)
|
||||
/**
|
||||
* @brief Represents bit stuffing error.
|
||||
*/
|
||||
#define CAN_ERR_PROT_STUFF (1 << 2)
|
||||
/**
|
||||
* @brief Identifies the controller is unable to send dominant bit.
|
||||
*/
|
||||
#define CAN_ERR_PROT_BIT0 (1 << 3)
|
||||
/**
|
||||
* @brief Identifies the controller is unable to send recessive bit.
|
||||
*/
|
||||
#define CAN_ERR_PROT_BIT1 (1 << 4)
|
||||
/**
|
||||
* @brief Represents bus overload.
|
||||
*/
|
||||
#define CAN_ERR_PROT_OVERLOAD (1 << 5)
|
||||
/**
|
||||
* @brief Represents active error announcement.
|
||||
*/
|
||||
#define CAN_ERR_PROT_ACTIVE (1 << 6)
|
||||
/**
|
||||
* @brief Identifies that rrror occurred on transmission.
|
||||
*/
|
||||
#define CAN_ERR_PROT_TX (1 << 7)
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CAN Protocol Violation Errors Location
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Represents unspecified protocol violation location.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_UNSPEC ( 0x00 )
|
||||
/**
|
||||
* @brief Represents protocol violation location at start of frame.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_SOF ( 0x01 )
|
||||
/**
|
||||
* @brief Represents protocol violation location at ID bits 0-4.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_ID0 ( 0x02 )
|
||||
/**
|
||||
* @brief Represents protocol violation location at ID bits 5-12.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_ID1 ( 0x03 )
|
||||
/**
|
||||
* @brief Represents protocol violation location at ID bits 13-17.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_ID2 ( 0x04 )
|
||||
/**
|
||||
* @brief Represents protocol violation location at ID bits 21-28.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_ID3 ( 0x05 )
|
||||
/**
|
||||
* @brief Represents protocol violation location at ID bits 18-20.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_ID4 ( 0x06 )
|
||||
/**
|
||||
* @brief Represents protocol violation location at IDE bit.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_IDE ( 0x07 )
|
||||
/**
|
||||
* @brief Represents protocol violation location at RTR bit.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_RTR ( 0x08 )
|
||||
/**
|
||||
* @brief Represents protocol violation location at SRTR bit.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_SRTR ( 0x09 )
|
||||
/**
|
||||
* @brief Represents protocol violation location at reserved bit 0.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_RES0 ( 0x0a )
|
||||
/**
|
||||
* @brief Represents protocol violation location at reserved bit 1.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_RES1 ( 0x0b )
|
||||
/**
|
||||
* @brief Represents protocol violation location at DLC.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_DLC ( 0x0c )
|
||||
/**
|
||||
* @brief Represents protocol violation location at data section.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_DATA ( 0x0d )
|
||||
/**
|
||||
* @brief Represents protocol violation location at CRC sequence.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_CRCSEQ ( 0x0e )
|
||||
/**
|
||||
* @brief Represents protocol violation location at CRC delimiter.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_CRCDEL ( 0x0f )
|
||||
/**
|
||||
* @brief Represents protocol violation location at ACK slot.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_ACK ( 0x10 )
|
||||
/**
|
||||
* @brief Represents protocol violation location at ACK delimiter.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_ACKDEL ( 0x11 )
|
||||
/**
|
||||
* @brief Represents protocol violation location at end of frame.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_EOF ( 0x12 )
|
||||
/**
|
||||
* @brief Represents protocol violation location at intermission.
|
||||
*/
|
||||
#define CAN_ERR_PROT_LOC_INTERM ( 0x13 )
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name CAN Transmission Errors
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Represents unspecified transmission error.
|
||||
*/
|
||||
#define CAN_ERR_TRX_UNSPEC ( 0x00 )
|
||||
/**
|
||||
* @brief Represents mask for CANH transmission error.
|
||||
*/
|
||||
#define CAN_ERR_TRX_H_MASK ( 0x0f )
|
||||
/**
|
||||
* @brief Represents no CAN high wire detected.
|
||||
*/
|
||||
#define CAN_ERR_TRX_H_NOWIRE ( 0x01 )
|
||||
/**
|
||||
* @brief Identifies CAN H shortage to bat.
|
||||
*/
|
||||
#define CAN_ERR_TRX_H_SHORT2BAT ( 0x02 )
|
||||
/**
|
||||
* @brief Identifies CAN H shortage to VCC.
|
||||
*/
|
||||
#define CAN_ERR_TRX_H_SHORT2VCC ( 0x03 )
|
||||
/**
|
||||
* @brief Identifies CAN H shortage to ground.
|
||||
*/
|
||||
#define CAN_ERR_TRX_H_SHORT2GND ( 0x04 )
|
||||
/**
|
||||
* @brief Represents mask for CANL transmission error.
|
||||
*/
|
||||
#define CAN_ERR_TRX_L_MASK ( 0xf0 )
|
||||
/**
|
||||
* @brief Represents no CAN low wire detected.
|
||||
*/
|
||||
#define CAN_ERR_TRX_L_NOWIRE ( 0x10 )
|
||||
/**
|
||||
* @brief Identifies CAN L shortage to bat.
|
||||
*/
|
||||
#define CAN_ERR_TRX_L_SHORT2BAT ( 0x20 )
|
||||
/**
|
||||
* @brief Identifies CAN L shortage to VCC.
|
||||
*/
|
||||
#define CAN_ERR_TRX_L_SHORT2VCC ( 0x30 )
|
||||
/**
|
||||
* @brief Identifies CAN L shortage to ground.
|
||||
*/
|
||||
#define CAN_ERR_TRX_L_SHORT2GND ( 0x40 )
|
||||
/**
|
||||
* @brief Identifies CAN L shortage to CAN H.
|
||||
*/
|
||||
#define CAN_ERR_TRX_L_SHORT2CANH ( 0x50 )
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief This structure represents the CAN message header.
|
||||
*
|
||||
*/
|
||||
struct can_frame_header {
|
||||
/**
|
||||
* @brief This member holds the CAN timestamp value.
|
||||
*/
|
||||
uint64_t timestamp;
|
||||
/**
|
||||
* @brief This member holds the CAN identifier value. Only first 29 bits
|
||||
* are be valid for CAN ID. In case 31st bit is set ( @ref CAN_ERR_ID_TAG ),
|
||||
* the frame is not valid. In this case, it might be an error frame.
|
||||
*/
|
||||
uint32_t can_id;
|
||||
/**
|
||||
* @brief This member holds the CAN flags. These are used to pass additional
|
||||
* information between driver and application. For possible flags, refer
|
||||
* to @ref CANFrameFlags group.
|
||||
*/
|
||||
uint16_t flags;
|
||||
/**
|
||||
* @brief This member holds the data length of CAN frame. This length
|
||||
* does not include size of the header.
|
||||
*/
|
||||
uint16_t dlen;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This structure represents one CAN frame. It consists of
|
||||
* CAN header and data.
|
||||
*/
|
||||
struct can_frame {
|
||||
/**
|
||||
* @brief This member stores the structure @ref can_frame_header
|
||||
* representing CAN header.
|
||||
*/
|
||||
struct can_frame_header header;
|
||||
/**
|
||||
* @brief This member stores CAN data.
|
||||
*/
|
||||
uint8_t data[CAN_FRAME_MAX_DLEN];
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This function computes the length of CAN frame.
|
||||
*
|
||||
* @param frame Pointer to the @ref can_frame structure.
|
||||
*
|
||||
* @return Length of CAN frame in bytes.
|
||||
*/
|
||||
static inline size_t can_framesize( struct can_frame *frame )
|
||||
{
|
||||
return offsetof( struct can_frame, data ) + frame->header.dlen;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif /* _DEV_CAN_CAN_FRAME_H */
|
||||
167
cpukit/include/dev/can/can-helpers.h
Normal file
167
cpukit/include/dev/can/can-helpers.h
Normal file
@@ -0,0 +1,167 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup CANFDStack
|
||||
*
|
||||
* @brief This header file is part of CAN/CAN FD bus common support. It
|
||||
* implements functions and defines used to simplify controller's
|
||||
* operations.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
|
||||
* Implementation is based on original LinCAN - Linux CAN bus driver
|
||||
* Part of OrtCAN project https://ortcan.sourceforge.net/
|
||||
* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://control.fel.cvut.cz>
|
||||
* Copyright (C) 2002-2024 Pavel Pisa <pisa@cmp.felk.cvut.cz>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _DEV_CAN_CAN_HELPERS_H
|
||||
#define _DEV_CAN_CAN_HELPERS_H
|
||||
|
||||
#include <rtems.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
/**
|
||||
* @brief This define provides user magic value. It is used to check whether
|
||||
* @ref rtems_can_user structure was correctly initialized.
|
||||
*/
|
||||
#define RTEMS_CAN_USER_MAGIC 0x05402033
|
||||
|
||||
#ifndef BIT
|
||||
#define BIT(nr) (1UL << (nr))
|
||||
#endif
|
||||
|
||||
#define GENMASK(h, l) (((~0UL) << (l)) & (~0UL >> (sizeof(long) * 8 - 1 - (h))))
|
||||
|
||||
#ifndef __bf_shf
|
||||
#define __bf_shf(x) (__builtin_ffsll(x) - 1)
|
||||
#endif
|
||||
|
||||
#ifndef FIELD_PREP
|
||||
#define FIELD_PREP(_mask, _val) \
|
||||
({ \
|
||||
((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \
|
||||
})
|
||||
#endif
|
||||
|
||||
#ifndef FIELD_GET
|
||||
#define FIELD_GET(_mask, _reg) \
|
||||
({ \
|
||||
(typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
|
||||
})
|
||||
#endif
|
||||
|
||||
static const uint8_t rtems_can_len2dlc[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, /* 0 - 8 */
|
||||
9, 9, 9, 9, /* 9 - 12 */
|
||||
10, 10, 10, 10, /* 13 - 16 */
|
||||
11, 11, 11, 11, /* 17 - 20 */
|
||||
12, 12, 12, 12, /* 21 - 24 */
|
||||
13, 13, 13, 13, 13, 13, 13, 13, /* 25 - 32 */
|
||||
14, 14, 14, 14, 14, 14, 14, 14, /* 33 - 40 */
|
||||
14, 14, 14, 14, 14, 14, 14, 14, /* 41 - 48 */
|
||||
15, 15, 15, 15, 15, 15, 15, 15, /* 49 - 56 */
|
||||
15, 15, 15, 15, 15, 15, 15, 15 /* 57 - 64 */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This function calculates CAN DLC from given length in bytes.
|
||||
*
|
||||
* @param len Length of the data in bytes.
|
||||
*
|
||||
* @return CAN data length code.
|
||||
*/
|
||||
static inline uint8_t rtems_canfd_len2dlc( uint8_t len )
|
||||
{
|
||||
if ( len > 64 ) {
|
||||
return 0xF;
|
||||
}
|
||||
|
||||
return rtems_can_len2dlc[len];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function atomically sets nth bit on given address.
|
||||
*
|
||||
* This function is a convinient wrapper around atomic_fetch_or().
|
||||
*
|
||||
* @param nr Bit shift value.
|
||||
* @param addr Adress where the @ref nr bit should be set.
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static inline void rtems_can_set_bit( int nr, atomic_uint *addr )
|
||||
{
|
||||
atomic_fetch_or( addr, 1 << nr );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function atomically clears nth bit on given address.
|
||||
*
|
||||
* This function is a convinient wrapper around atomic_fetch_and().
|
||||
*
|
||||
* @param nr Bit shift value.
|
||||
* @param addr Adress where the @ref nr bit should be clear.
|
||||
*
|
||||
* @return None
|
||||
*/
|
||||
static inline void rtems_can_clear_bit( int nr, atomic_uint *addr )
|
||||
{
|
||||
atomic_fetch_and( addr, ~( 1 << nr ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function atomically tests whether nth bit on given address
|
||||
* is set.
|
||||
*
|
||||
* This function is a convinient wrapper around atomic_load().
|
||||
*
|
||||
* @param nr Bit shift value.
|
||||
* @param addr Adress where the @ref nr bit should be tested.
|
||||
*
|
||||
* @return 1 if set, 0 otherwise.
|
||||
*/
|
||||
static inline int rtems_can_test_bit( int nr, atomic_uint *addr )
|
||||
{
|
||||
return ( atomic_load( addr ) ) & ( 1 << nr ) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function atomically tests and sets nth bit on given address.
|
||||
*
|
||||
* This function is a convinient wrapper around atomic_fetch_or().
|
||||
*
|
||||
* @param nr Bit shift value.
|
||||
* @param addr Adress where the @ref nr bit should be tested and set.
|
||||
*
|
||||
* @return 1 if set, 0 otherwise.
|
||||
*/
|
||||
static inline int rtems_can_test_and_set_bit( int nr, atomic_uint *addr )
|
||||
{
|
||||
return ( atomic_fetch_or( addr, 1 << nr ) & ( 1 << nr ) ) ? 1 : 0;
|
||||
}
|
||||
|
||||
#endif /* _DEV_CAN_CAN_HELPERS_H */
|
||||
728
cpukit/include/dev/can/can-impl.h
Normal file
728
cpukit/include/dev/can/can-impl.h
Normal file
@@ -0,0 +1,728 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup CANFDStack
|
||||
*
|
||||
* @brief This file is part of CAN/CAN FD bus common support
|
||||
* and implements internal functions for CAN queues handling
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
|
||||
* Implementation is based on original LinCAN - Linux CAN bus driver
|
||||
* Part of OrtCAN project https://ortcan.sourceforge.net/
|
||||
* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://control.fel.cvut.cz>
|
||||
* Copyright (C) 2002-2024 Pavel Pisa <pisa@cmp.felk.cvut.cz>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __DEV_CAN_CAN_IMPL_H
|
||||
#define __DEV_CAN_CAN_IMPL_H
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/queue.h>
|
||||
#include <rtems.h>
|
||||
#include <rtems/timespec.h>
|
||||
#include <rtems/status-checks.h>
|
||||
#include <rtems/thread.h>
|
||||
|
||||
#include <dev/can/can-frame.h>
|
||||
#include <dev/can/can-filter.h>
|
||||
#include <dev/can/can-queue.h>
|
||||
|
||||
/**
|
||||
* @brief This function atomically tests FIFO flag to input flag.
|
||||
*
|
||||
* @param fifo Pointer to CAN FIFO Queue.
|
||||
* @param flag Required flag to be tested.
|
||||
*
|
||||
* @return True if FIFO has the flag.
|
||||
* @return False otherwise.
|
||||
*
|
||||
*/
|
||||
static inline int rtems_can_queue_fifo_test_flag(
|
||||
struct rtems_can_queue_fifo *fifo,
|
||||
int flag
|
||||
)
|
||||
{
|
||||
return ( atomic_load( &fifo->fifo_flags ) & flag ) == flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function atomically sets FIFO flag.
|
||||
*
|
||||
* @param fifo Pointer to CAN FIFO Queue.
|
||||
* @param flag Flag to be set.
|
||||
*
|
||||
* @return None.
|
||||
*
|
||||
*/
|
||||
static inline void rtems_can_queue_fifo_set_flag(
|
||||
struct rtems_can_queue_fifo *fifo,
|
||||
int flag
|
||||
)
|
||||
{
|
||||
atomic_fetch_or( &fifo->fifo_flags, flag );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function atomically sets FIFO flag.
|
||||
*
|
||||
* @param fifo Pointer to CAN FIFO Queue.
|
||||
* @param flag Flag to be set.
|
||||
*
|
||||
* @return None.
|
||||
*
|
||||
*/
|
||||
static inline void rtems_can_queue_fifo_clear_flag(
|
||||
struct rtems_can_queue_fifo *fifo,
|
||||
int flag
|
||||
)
|
||||
{
|
||||
atomic_fetch_and( &fifo->fifo_flags, ~flag );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function atomically tests and sets FIFO flag
|
||||
*
|
||||
* @param fifo Pointer to CAN FIFO Queue.
|
||||
* @param flag Flag to be tested and set.
|
||||
*
|
||||
* @return True if FIFO has the flag.
|
||||
* @return False otherwise.
|
||||
*
|
||||
*/
|
||||
static inline int rtems_can_queue_fifo_test_and_set_flag(
|
||||
struct rtems_can_queue_fifo *fifo,
|
||||
int flag
|
||||
)
|
||||
{
|
||||
return ( atomic_fetch_or( &fifo->fifo_flags, flag ) & flag ) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function atomically tests and clears FIFO flag
|
||||
*
|
||||
* @param fifo Pointer to CAN FIFO Queue.
|
||||
* @param flag Flag to be tested and cleared.
|
||||
*
|
||||
* @return True if FIFO has the flag.
|
||||
* @return False otherwise.
|
||||
*
|
||||
*/
|
||||
static inline int rtems_can_queue_fifo_test_and_clear_fl(
|
||||
struct rtems_can_queue_fifo *fifo,
|
||||
int flag
|
||||
)
|
||||
{
|
||||
return ( atomic_fetch_and( &fifo->fifo_flags, ~flag ) & flag ) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This fumction tests if there is ready slot to be taken by
|
||||
* @ref rtems_can_queue_fifo_test_outslot .
|
||||
*
|
||||
* @param fifo Pointer to CAN FIFO Queue.
|
||||
*
|
||||
* @return True if FIFO has slot which .
|
||||
* @return False otherwise.
|
||||
*
|
||||
*/
|
||||
static inline int rtems_can_queue_fifo_out_is_ready_unprotected(
|
||||
struct rtems_can_queue_fifo *fifo
|
||||
)
|
||||
{
|
||||
return ( ( fifo->head ) != NULL ) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function checks whether CAN Frame identifier and flags match
|
||||
* with queue filter.
|
||||
*
|
||||
* It is used to filter frame to edges based on CAN identifier and flag
|
||||
* match with queue filter.
|
||||
*
|
||||
* @param filter Pointer to @ref rtems_can_filter structure.
|
||||
* @param id CAN frame identifier.
|
||||
* @param flags CAN frame flags.
|
||||
*
|
||||
* @return True if match, false otherwise.
|
||||
*
|
||||
*/
|
||||
static inline bool rtems_can_queue_filter_match(
|
||||
struct rtems_can_filter *filter,
|
||||
uint32_t id,
|
||||
uint32_t flags )
|
||||
{
|
||||
return ( ( filter->id^id ) & filter->id_mask ) ||
|
||||
( ( filter->flags^flags ) & filter->flags_mask ) ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function allocates slot for the input of one CAN message
|
||||
*
|
||||
* @param fifo Pointer to the FIFO structure
|
||||
* @param slotp Pointer to location to store pointer to the allocated slot.
|
||||
* @param cmd Optional command associated with allocated slot.
|
||||
*
|
||||
* @return The function returns negative value if there is no
|
||||
* free slot in the FIFO queue. Zero if free slot was found.
|
||||
*/
|
||||
static inline int rtems_can_queue_fifo_get_inslot(
|
||||
struct rtems_can_queue_fifo *fifo,
|
||||
struct rtems_can_queue_slot **slotp,
|
||||
int cmd
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_slot *slot;
|
||||
rtems_mutex_lock( &fifo->fifo_lock );
|
||||
|
||||
/* Get the first free slot slot from free_list */
|
||||
if ( ( slot = fifo->free_list ) == NULL ) {
|
||||
rtems_can_queue_fifo_set_flag( fifo, RTEMS_CAN_FIFOF_OVERRUN );
|
||||
rtems_can_queue_fifo_set_flag( fifo, RTEMS_CAN_FIFOF_FULL );
|
||||
rtems_mutex_unlock( &fifo->fifo_lock );
|
||||
*slotp = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Adjust free slot list */
|
||||
if ( ( fifo->free_list = slot->next ) == NULL ) {
|
||||
rtems_can_queue_fifo_set_flag( fifo, RTEMS_CAN_FIFOF_FULL );
|
||||
}
|
||||
|
||||
rtems_mutex_unlock( &fifo->fifo_lock );
|
||||
*slotp = slot;
|
||||
slot->slot_flags = cmd & RTEMS_CAN_SLOTF_CMD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function releases slot for the further processing.
|
||||
*
|
||||
* @param fifo Pointer to the FIFO structure
|
||||
* @param slot Pointer to the slot previously acquired by
|
||||
* @ref rtems_can_queue_fifo_get_inslot .
|
||||
*
|
||||
* @return The nonzero return value indicates, that the queue was empty
|
||||
* before call to the function. The caller should wake-up output side
|
||||
* of the queue.
|
||||
*
|
||||
*/
|
||||
static inline int rtems_can_queue_fifo_put_inslot(
|
||||
struct rtems_can_queue_fifo *fifo,
|
||||
struct rtems_can_queue_slot *slot
|
||||
)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
slot->next = NULL;
|
||||
rtems_mutex_lock( &fifo->fifo_lock );
|
||||
*fifo->tail = slot;
|
||||
fifo->tail = &slot->next;
|
||||
|
||||
if ( rtems_can_queue_fifo_test_and_clear_fl( fifo, RTEMS_CAN_FIFOF_EMPTY ) ) {
|
||||
/* Fifo has been empty before put */
|
||||
ret = RTEMS_CAN_FIFOF_EMPTY;
|
||||
}
|
||||
|
||||
if ( rtems_can_queue_fifo_test_and_clear_fl( fifo, RTEMS_CAN_FIFOF_INACTIVE ) ) {
|
||||
/* Fifo has been empty before put */
|
||||
ret |= RTEMS_CAN_FIFOF_INACTIVE;
|
||||
}
|
||||
|
||||
if ( rtems_can_queue_fifo_test_and_clear_fl( fifo, RTEMS_CAN_FIFOF_OVERRUN ) ) {
|
||||
/* RX overflow occured, report it with frame flag */
|
||||
slot->frame.header.flags |= CAN_FRAME_FIFO_OVERFLOW;
|
||||
}
|
||||
|
||||
rtems_mutex_unlock( &fifo->fifo_lock );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function releases and aborts slot.
|
||||
*
|
||||
* @param fifo Pointer to the FIFO structure.
|
||||
* @param slot Pointer to the slot previously acquired by
|
||||
* @ref rtems_can_queue_fifo_get_inslot .
|
||||
*
|
||||
* @return The nonzero value indicates that fifo was full.
|
||||
*
|
||||
*/
|
||||
static inline int rtems_can_queue_fifo_abort_inslot(
|
||||
struct rtems_can_queue_fifo *fifo,
|
||||
struct rtems_can_queue_slot *slot
|
||||
)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
rtems_mutex_lock( &fifo->fifo_lock );
|
||||
slot->next = fifo->free_list;
|
||||
fifo->free_list = slot;
|
||||
if ( rtems_can_queue_fifo_test_and_clear_fl( fifo, RTEMS_CAN_FIFOF_FULL ) ) {
|
||||
ret = RTEMS_CAN_FIFOF_FULL;
|
||||
}
|
||||
|
||||
rtems_mutex_unlock( &fifo->fifo_lock );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function tests and gets ready slot from the FIFO.
|
||||
*
|
||||
* @param fifo Pointer to the FIFO structure.
|
||||
* @param slotp Pointer to location to store pointer to the oldest slot
|
||||
* from the FIFO.
|
||||
*
|
||||
* @return The negative value indicates, that queue is empty.
|
||||
* The positive or zero value represents command stored into slot by
|
||||
* the call to the function @ref rtems_can_queue_fifo_get_inslot .
|
||||
* The successfully acquired FIFO output slot has to be released by
|
||||
* the call @ref rtems_can_queue_fifo_free_outslot or
|
||||
* @ref rtems_can_queue_fifo_again_outslot
|
||||
*
|
||||
*/
|
||||
static inline int rtems_can_queue_fifo_test_outslot(
|
||||
struct rtems_can_queue_fifo *fifo,
|
||||
struct rtems_can_queue_slot **slotp
|
||||
)
|
||||
{
|
||||
int cmd;
|
||||
struct rtems_can_queue_slot *slot;
|
||||
|
||||
rtems_mutex_lock( &fifo->fifo_lock );
|
||||
if ( ( slot = fifo->head ) == NULL ) {
|
||||
rtems_mutex_unlock( &fifo->fifo_lock );
|
||||
*slotp = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( ( fifo->head=slot->next ) == NULL ) {
|
||||
fifo->tail = &fifo->head;
|
||||
}
|
||||
|
||||
fifo->out_taken += 1;
|
||||
|
||||
rtems_mutex_unlock( &fifo->fifo_lock );
|
||||
|
||||
*slotp = slot;
|
||||
cmd = slot->slot_flags;
|
||||
|
||||
return ( cmd & RTEMS_CAN_SLOTF_CMD );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function frees FIFO slot after processing.
|
||||
*
|
||||
* @param fifo Pointer to the FIFO structure.
|
||||
* @param slot Pointer to the slot previously acquired by
|
||||
* rtems_can_queue_fifo_test_outslot().
|
||||
*
|
||||
* @return The returned value informs about FIFO state change.
|
||||
* The mask %RTEMS_CAN_FIFOF_FULL indicates, that the FIFO was full before
|
||||
* the function call. The mask %RTEMS_CAN_FIFOF_EMPTY informs, that last
|
||||
* ready slot has been processed.
|
||||
*
|
||||
*/
|
||||
static inline int rtems_can_queue_fifo_free_outslot(
|
||||
struct rtems_can_queue_fifo *fifo,
|
||||
struct rtems_can_queue_slot *slot
|
||||
)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
rtems_mutex_lock( &fifo->fifo_lock );
|
||||
slot->next = fifo->free_list;
|
||||
fifo->free_list = slot;
|
||||
if ( rtems_can_queue_fifo_test_and_clear_fl( fifo, RTEMS_CAN_FIFOF_FULL ) ) {
|
||||
ret = RTEMS_CAN_FIFOF_FULL;
|
||||
}
|
||||
|
||||
fifo->out_taken -= 1;
|
||||
|
||||
if ( ( fifo->head ) == NULL ) {
|
||||
rtems_can_queue_fifo_set_flag( fifo, RTEMS_CAN_FIFOF_INACTIVE );
|
||||
ret |= RTEMS_CAN_FIFOF_INACTIVE;
|
||||
if ( fifo->out_taken == 0 ) {
|
||||
rtems_can_queue_fifo_set_flag( fifo, RTEMS_CAN_FIFOF_EMPTY );
|
||||
ret |= RTEMS_CAN_FIFOF_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
rtems_mutex_unlock( &fifo->fifo_lock );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function returns back FIFO slot to postpone its processing.
|
||||
*
|
||||
* @param fifo Pointer to the FIFO structure.
|
||||
* @param slot Pointer to the slot previously acquired by
|
||||
* rtems_can_queue_fifo_test_outslot().
|
||||
*
|
||||
* @return This function returns %RTEMS_CAN_FIFOF_INACTIV if there
|
||||
* has not been ready slot before the call.
|
||||
*
|
||||
*/
|
||||
static inline int rtems_can_queue_fifo_again_outslot(
|
||||
struct rtems_can_queue_fifo *fifo,
|
||||
struct rtems_can_queue_slot *slot
|
||||
)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
rtems_mutex_lock( &fifo->fifo_lock );
|
||||
if ( ( slot->next = fifo->head ) == NULL ) {
|
||||
fifo->tail = &slot->next;
|
||||
ret = RTEMS_CAN_FIFOF_INACTIVE;
|
||||
}
|
||||
|
||||
fifo->out_taken -= 1;
|
||||
|
||||
fifo->head = slot;
|
||||
rtems_mutex_unlock( &fifo->fifo_lock );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function calculates size of FIFO slot
|
||||
*
|
||||
* @param max_data_length Maximum CAN frame data length.
|
||||
*
|
||||
* @return Returns size of one FIFO slot
|
||||
*
|
||||
*/
|
||||
static inline int rtems_can_queue_fifo_slot_size( int max_data_length )
|
||||
{
|
||||
return RTEMS_ALIGN_UP(
|
||||
offsetof( struct rtems_can_queue_slot, frame.data ) + max_data_length,
|
||||
RTEMS_ALIGNOF(struct rtems_can_queue_slot)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function provides request to send notification to the
|
||||
* input ends.
|
||||
*
|
||||
* @param qedge Pointer to the edge structure.
|
||||
* @param what Notification type.
|
||||
*
|
||||
* @return None
|
||||
*
|
||||
*/
|
||||
static inline void rtems_can_queue_notify_input_ends(
|
||||
struct rtems_can_queue_edge *qedge,
|
||||
int what
|
||||
)
|
||||
{
|
||||
if ( qedge->input_ends ) {
|
||||
if ( qedge->input_ends->notify ) {
|
||||
qedge->input_ends->notify( qedge->input_ends, qedge,what );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function provides request to send notification to the
|
||||
* output ends.
|
||||
*
|
||||
* @param qedge Pointer to the edge structure.
|
||||
* @param what Notification type.
|
||||
*
|
||||
* @return None
|
||||
*
|
||||
*/
|
||||
static inline void rtems_can_queue_notify_output_ends(
|
||||
struct rtems_can_queue_edge *qedge,
|
||||
int what
|
||||
)
|
||||
{
|
||||
if ( qedge->output_ends ) {
|
||||
if ( qedge->output_ends->notify ) {
|
||||
qedge->output_ends->notify( qedge->output_ends, qedge, what );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function provides request to send notification to both ends.
|
||||
*
|
||||
* @param qedge Pointer to the edge structure.
|
||||
* @param what Notification type.
|
||||
*
|
||||
* @return None
|
||||
*
|
||||
*/
|
||||
static inline void rtems_can_queue_notify_both_ends(
|
||||
struct rtems_can_queue_edge *qedge,
|
||||
int what
|
||||
)
|
||||
{
|
||||
rtems_can_queue_notify_input_ends( qedge, what );
|
||||
rtems_can_queue_notify_output_ends( qedge, what );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function marks output end of the edge as active.
|
||||
*
|
||||
* Function call moves output side of the edge from idle onto active edges
|
||||
* list. This function has to be called with edge reference count held.
|
||||
* that is same as for most of other edge functions.
|
||||
*
|
||||
* @param qedge Pointer to the edge structure.
|
||||
* @param input_ends Input side of the edge
|
||||
*
|
||||
* @return None
|
||||
*
|
||||
*/
|
||||
static inline void rtems_can_queue_activate_edge(
|
||||
struct rtems_can_queue_ends *input_ends,
|
||||
struct rtems_can_queue_edge *qedge
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_ends *output_ends = qedge->output_ends;
|
||||
|
||||
if ( qedge->edge_prio >= RTEMS_CAN_QUEUE_PRIO_NR ) {
|
||||
qedge->edge_prio = RTEMS_CAN_QUEUE_PRIO_NR - 1;
|
||||
}
|
||||
|
||||
if ( output_ends != NULL ) {
|
||||
rtems_mutex_lock( &output_ends->ends_lock );
|
||||
rtems_mutex_lock( &qedge->fifo.fifo_lock );
|
||||
if ( qedge->peershead != &output_ends->active[qedge->edge_prio] ) {
|
||||
if ( qedge->peershead != NULL ) {
|
||||
TAILQ_REMOVE( qedge->peershead, qedge, activepeers );
|
||||
}
|
||||
|
||||
TAILQ_INSERT_HEAD(
|
||||
&output_ends->active[qedge->edge_prio],
|
||||
qedge, activepeers
|
||||
);
|
||||
qedge->peershead = &output_ends->active[qedge->edge_prio];
|
||||
}
|
||||
rtems_mutex_unlock( &qedge->fifo.fifo_lock );
|
||||
rtems_mutex_unlock( &output_ends->ends_lock );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void rtems_can_queue_edge_to_idle_unprotected(
|
||||
struct rtems_can_queue_ends *output_ends,
|
||||
struct rtems_can_queue_edge *qedge
|
||||
)
|
||||
{
|
||||
if ( qedge->peershead != NULL ) {
|
||||
TAILQ_REMOVE( qedge->peershead, qedge, activepeers );
|
||||
}
|
||||
TAILQ_INSERT_TAIL( &output_ends->idle, qedge, activepeers );
|
||||
qedge->peershead = &output_ends->idle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function increments edge reference count
|
||||
*
|
||||
* @param edge Pointer to the edge structure
|
||||
*
|
||||
* @return None
|
||||
*
|
||||
*/
|
||||
static inline void rtems_can_queue_edge_incref(
|
||||
struct rtems_can_queue_edge *edge
|
||||
)
|
||||
{
|
||||
atomic_fetch_add( &edge->edge_used, 1 );
|
||||
}
|
||||
|
||||
static inline void rtems_can_queue_do_edge_decref_body(
|
||||
struct rtems_can_queue_edge *edge
|
||||
)
|
||||
{
|
||||
int dead_fl = 0;
|
||||
struct rtems_can_queue_ends *input_ends=edge->input_ends;
|
||||
struct rtems_can_queue_ends *output_ends=edge->output_ends;
|
||||
|
||||
if ( input_ends < output_ends ) {
|
||||
rtems_mutex_lock( &input_ends->ends_lock );
|
||||
rtems_mutex_lock( &output_ends->ends_lock );
|
||||
if ( atomic_fetch_sub( &edge->edge_used, 1 ) == 1 ) {
|
||||
dead_fl = !rtems_can_queue_fifo_test_and_set_flag(
|
||||
&edge->fifo,
|
||||
RTEMS_CAN_FIFOF_DEAD
|
||||
);
|
||||
}
|
||||
rtems_mutex_unlock( &output_ends->ends_lock );
|
||||
rtems_mutex_unlock( &input_ends->ends_lock );
|
||||
} else {
|
||||
rtems_mutex_lock( &output_ends->ends_lock );
|
||||
if ( output_ends != input_ends ) {
|
||||
rtems_mutex_lock( &input_ends->ends_lock );
|
||||
}
|
||||
if ( atomic_fetch_sub( &edge->edge_used, 1 ) == 1 ) {
|
||||
dead_fl = !rtems_can_queue_fifo_test_and_set_flag(
|
||||
&edge->fifo,
|
||||
RTEMS_CAN_FIFOF_DEAD
|
||||
);
|
||||
}
|
||||
if ( output_ends != input_ends ) {
|
||||
rtems_mutex_unlock( &input_ends->ends_lock );
|
||||
}
|
||||
rtems_mutex_unlock( &output_ends->ends_lock );
|
||||
}
|
||||
|
||||
if ( dead_fl ) {
|
||||
rtems_can_queue_edge_do_dead( edge );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function decrements edge reference count.
|
||||
*
|
||||
* This function has to be called without lock held for both ends of edge.
|
||||
* If reference count drops to 0, function rtems_can_queue_edge_do_dead()
|
||||
* is called.
|
||||
*
|
||||
* @param edge Pointer to the edge structure
|
||||
*
|
||||
* @return None
|
||||
*
|
||||
*/
|
||||
static inline void rtems_can_queue_edge_decref(
|
||||
struct rtems_can_queue_edge *edge
|
||||
)
|
||||
{
|
||||
unsigned int x;
|
||||
|
||||
x = atomic_load_explicit( &edge->edge_used, memory_order_relaxed );
|
||||
|
||||
do {
|
||||
if ( x <= 1 ) {
|
||||
return rtems_can_queue_do_edge_decref( edge );
|
||||
}
|
||||
} while( !atomic_compare_exchange_strong(&edge->edge_used, &x, x-1 ) );
|
||||
}
|
||||
|
||||
static inline struct rtems_can_queue_edge *rtems_can_queue_first_inedge(
|
||||
struct rtems_can_queue_ends *qends
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_edge *edge;
|
||||
|
||||
rtems_mutex_lock( &qends->ends_lock );
|
||||
edge = TAILQ_FIRST( &qends->inlist );
|
||||
|
||||
while ( edge && rtems_can_queue_fifo_test_flag( &edge->fifo, RTEMS_CAN_FIFOF_DEAD ) ) {
|
||||
edge = TAILQ_NEXT( edge, input_peers );
|
||||
}
|
||||
if ( edge )
|
||||
rtems_can_queue_edge_incref( edge );
|
||||
|
||||
rtems_mutex_unlock( &qends->ends_lock );
|
||||
return edge;
|
||||
}
|
||||
|
||||
|
||||
static inline struct rtems_can_queue_edge *rtems_can_queue_next_inedge(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge *edge
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_edge *next;
|
||||
|
||||
rtems_mutex_lock( &qends->ends_lock );
|
||||
next = TAILQ_NEXT( edge, input_peers );
|
||||
|
||||
while ( next && rtems_can_queue_fifo_test_flag( &next->fifo, RTEMS_CAN_FIFOF_DEAD ) ) {
|
||||
next = TAILQ_NEXT( next, input_peers );
|
||||
}
|
||||
if ( next )
|
||||
rtems_can_queue_edge_incref( next );
|
||||
|
||||
rtems_mutex_unlock( &qends->ends_lock );
|
||||
rtems_can_queue_edge_decref( edge );
|
||||
return next;
|
||||
}
|
||||
|
||||
#define rtems_can_queue_for_each_inedge( qends, edge ) \
|
||||
for ( \
|
||||
edge = rtems_can_queue_first_inedge( qends ); \
|
||||
edge; \
|
||||
edge = rtems_can_queue_next_inedge( qends, edge ) )
|
||||
|
||||
static inline
|
||||
struct rtems_can_queue_edge *can_queue_first_outedge(
|
||||
struct rtems_can_queue_ends *qends
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_edge *edge;
|
||||
|
||||
rtems_mutex_lock( &qends->ends_lock );
|
||||
edge = TAILQ_FIRST( &qends->outlist );
|
||||
|
||||
while ( edge && rtems_can_queue_fifo_test_flag( &edge->fifo, RTEMS_CAN_FIFOF_DEAD ) ) {
|
||||
edge = TAILQ_NEXT( edge, output_peers );
|
||||
}
|
||||
if ( edge )
|
||||
rtems_can_queue_edge_incref( edge );
|
||||
|
||||
rtems_mutex_unlock( &qends->ends_lock );
|
||||
return edge;
|
||||
}
|
||||
|
||||
static inline
|
||||
struct rtems_can_queue_edge *rtems_can_queue_next_outedge(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge *edge
|
||||
)
|
||||
{
|
||||
struct rtems_can_queue_edge *next;
|
||||
|
||||
rtems_mutex_lock( &qends->ends_lock );
|
||||
next = TAILQ_NEXT( edge, output_peers );
|
||||
|
||||
while ( next && rtems_can_queue_fifo_test_flag( &next->fifo, RTEMS_CAN_FIFOF_DEAD ) ) {
|
||||
next = TAILQ_NEXT( next, output_peers );
|
||||
}
|
||||
if ( next )
|
||||
rtems_can_queue_edge_incref( next );
|
||||
|
||||
rtems_mutex_unlock( &qends->ends_lock );
|
||||
rtems_can_queue_edge_decref( edge );
|
||||
return next;
|
||||
}
|
||||
|
||||
#define rtems_can_queue_for_each_outedge( qends, edge ) \
|
||||
for ( \
|
||||
edge = can_queue_first_outedge( qends ); \
|
||||
edge; \
|
||||
edge = rtems_can_queue_next_outedge( qends, edge ) )
|
||||
|
||||
|
||||
#endif /* __DEV_CAN_CAN_IMPL_H */
|
||||
845
cpukit/include/dev/can/can-queue.h
Normal file
845
cpukit/include/dev/can/can-queue.h
Normal file
@@ -0,0 +1,845 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup CANFDStack
|
||||
*
|
||||
* @brief This file is part of CAN/CAN FD bus common support
|
||||
* and implements CAN FIFOs and generic hubs/ends
|
||||
* for chip and caracter driver interface sides.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
|
||||
* Implementation is based on original LinCAN - Linux CAN bus driver
|
||||
* Part of OrtCAN project https://ortcan.sourceforge.net/
|
||||
* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://control.fel.cvut.cz>
|
||||
* Copyright (C) 2002-2024 Pavel Pisa <pisa@cmp.felk.cvut.cz>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __DEV_CAN_CAN_QUEUE_H
|
||||
#define __DEV_CAN_CAN_QUEUE_H
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/queue.h>
|
||||
#include <rtems.h>
|
||||
#include <rtems/timespec.h>
|
||||
#include <rtems/status-checks.h>
|
||||
#include <rtems/thread.h>
|
||||
|
||||
#include <dev/can/can-frame.h>
|
||||
#include <dev/can/can-filter.h>
|
||||
|
||||
/**
|
||||
* @brief This structure represents one CAN message slot in the CAN FIFO queue.
|
||||
*/
|
||||
struct rtems_can_queue_slot {
|
||||
/**
|
||||
* @brief Pointer to next/younger slot.
|
||||
*/
|
||||
struct rtems_can_queue_slot *next;
|
||||
/**
|
||||
* @brief Space for flags and optional command describing action
|
||||
* associated with slot data
|
||||
*/
|
||||
atomic_uint slot_flags;
|
||||
/**
|
||||
* @brief This member holds @ref can_frame structure representing one
|
||||
* CAN frame/message.
|
||||
*/
|
||||
struct can_frame frame;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Macro for command associated with allocated slot.
|
||||
*/
|
||||
#define RTEMS_CAN_SLOTF_CMD ( 0x00ff )
|
||||
|
||||
/**
|
||||
* @brief This structure represents CAN FIFO queue. It is implemented as
|
||||
* a single linked list of slots prepared for processing. The empty slots
|
||||
* are stored in single linked list.
|
||||
*
|
||||
*/
|
||||
struct rtems_can_queue_fifo {
|
||||
/**
|
||||
* @brief This field holds global flags describing state of the FIFO.
|
||||
*/
|
||||
atomic_uint fifo_flags;
|
||||
/**
|
||||
* @brief This member holds the number of elements in the fifo.
|
||||
*
|
||||
* This is only the number of slots taken on the out side which
|
||||
* processing has not been finished by chip driver (or copy to user
|
||||
* in another direction). These has to be accounted because FIFI is
|
||||
* not empty even that there are no slots on the linked list. Only when
|
||||
* all slots taken by out side are processed then it can be signaled
|
||||
* as empty.
|
||||
*/
|
||||
unsigned int out_taken;
|
||||
/**
|
||||
* @brief This member holds futher description of error condition.
|
||||
*/
|
||||
unsigned long error_code;
|
||||
/**
|
||||
* @brief This member holds the pointer to the FIFO head, oldest slot.
|
||||
*/
|
||||
struct rtems_can_queue_slot *head;
|
||||
/**
|
||||
* @brief This member holds the pointer to the location, where pointer to
|
||||
* newly inserted slot should be added.
|
||||
*/
|
||||
struct rtems_can_queue_slot **tail;
|
||||
/**
|
||||
* @brief This member holds the pointer to list of the free slots
|
||||
* associated with queue.
|
||||
*/
|
||||
struct rtems_can_queue_slot *free_list;
|
||||
/**
|
||||
* @brief This member holds the pointer to the memory allocated for
|
||||
* the list slots.
|
||||
*/
|
||||
struct rtems_can_queue_slot *entry;
|
||||
/**
|
||||
* @brief This member holds maximum data length of one CAN frame.
|
||||
*/
|
||||
int max_data_length;
|
||||
/**
|
||||
* @brief This member holds the number of allocated slots.
|
||||
*/
|
||||
int allocated_slot_count;
|
||||
/**
|
||||
* @brief This member holds the lock to ensure atomicity of slot
|
||||
* manipulation operations.
|
||||
*/
|
||||
rtems_mutex fifo_lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This is set when FIFO is scheduled for destruction.
|
||||
*/
|
||||
#define RTEMS_CAN_FIFOF_DESTROY ( 1 << 15 )
|
||||
/**
|
||||
* @brief This is set when some error condition occurs.
|
||||
*/
|
||||
#define RTEMS_CAN_FIFOF_ERROR ( 1 << 14 )
|
||||
/**
|
||||
* @brief This defines that error should lead to the FIFO block state.
|
||||
*/
|
||||
#define RTEMS_CAN_FIFOF_ERR2BLOCK ( 1 << 13 )
|
||||
/**
|
||||
* @brief This state blocks insertion of the next messages.
|
||||
*/
|
||||
#define RTEMS_CAN_FIFOF_BLOCK ( 1 << 12 )
|
||||
/**
|
||||
* @brief This indicates attempt to acquire new slot, when FIFO is full.
|
||||
*/
|
||||
#define RTEMS_CAN_FIFOF_OVERRUN ( 1 << 11 )
|
||||
/**
|
||||
* @brief This indicates FIFO full state.
|
||||
*/
|
||||
#define RTEMS_CAN_FIFOF_FULL ( 1 << 10 )
|
||||
/**
|
||||
* @brief This indicates no allocated slot in the FIFO.
|
||||
*/
|
||||
#define RTEMS_CAN_FIFOF_EMPTY ( 1 << 9 )
|
||||
/**
|
||||
* @brief This is used when FIFO is beeing destroyed.
|
||||
*/
|
||||
#define RTEMS_CAN_FIFOF_DEAD ( 1 << 8 )
|
||||
/**
|
||||
* @brief This indicates FIFO is inactive.
|
||||
*/
|
||||
#define RTEMS_CAN_FIFOF_INACTIVE ( 1 << 7 )
|
||||
/**
|
||||
* @brief This indicates FIFO was freed when being empty.
|
||||
*/
|
||||
#define RTEMS_CAN_FIFOF_FREEONEMPTY ( 1 << 6 )
|
||||
/**
|
||||
* @brief This indicates FIFO is ready.
|
||||
*/
|
||||
#define RTEMS_CAN_FIFOF_READY ( 1 << 5 )
|
||||
/**
|
||||
* @brief This indicates pending notification.
|
||||
*/
|
||||
#define RTEMS_CAN_FIFOF_NOTIFYPEND ( 1 << 4 )
|
||||
|
||||
/**
|
||||
* @brief This function frees all ready slots from the FIFO.
|
||||
*
|
||||
* The caller should be prepared to handle situations, when some
|
||||
* slots are held by input or output side slots processing.
|
||||
* These slots cannot be flushed or their processing interrupted.
|
||||
*
|
||||
* @param fifo Pointer to the FIFO structure.
|
||||
*
|
||||
* @return The nonzero value indicates that queue has not been
|
||||
* empty before the function call.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_fifo_flush_slots( struct rtems_can_queue_fifo *fifo );
|
||||
|
||||
/**
|
||||
* @brief This function initializes slot chain of one CAN FIFO.
|
||||
*
|
||||
* The caller should be prepared to handle situations, when some
|
||||
* slots are held by input or output side slots processing.
|
||||
* These slots cannot be flushed or their processing interrupted.
|
||||
*
|
||||
* @param fifo Pointer to the FIFO structure.
|
||||
*
|
||||
* @return The negative value indicates that there is no memory
|
||||
* to allocate space for the requested number of the slots.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_fifo_init_slots( struct rtems_can_queue_fifo *fifo );
|
||||
|
||||
struct rtems_can_queue_edge;
|
||||
TAILQ_HEAD( rtems_can_queue_edges_list, rtems_can_queue_edge );
|
||||
|
||||
/**
|
||||
* @brief This structure represents one direction connection from messages
|
||||
* source ( @ref input_ends) to message consumer ( @ref output_ends) fifo
|
||||
* ends hub. The edge contains @ref rtems_can_queue_fifo for message fifo
|
||||
* implementation-
|
||||
*
|
||||
*/
|
||||
struct rtems_can_queue_edge {
|
||||
/**
|
||||
* @brief This member holds place where primitive @ref rtems_can_queue_fifo
|
||||
* FIFO is located.
|
||||
*/
|
||||
struct rtems_can_queue_fifo fifo;
|
||||
/**
|
||||
* @brief This member holds place where primitive @ref rtems_can_filter is
|
||||
* located.
|
||||
*/
|
||||
struct rtems_can_filter filter;
|
||||
/**
|
||||
* @brief This member holds the lists of all peers FIFOs connected by
|
||||
* their input side ( @ref input_ends ) to the same terminal
|
||||
* ( @ref rtems_can_queue_ends ).
|
||||
*/
|
||||
TAILQ_ENTRY( rtems_can_queue_edge ) input_peers;
|
||||
/**
|
||||
* @brief This member holds the lists of all peers FIFOs connected by their
|
||||
* output side ( @ref output_ends ) to the same terminal
|
||||
* ( @ref rtems_can_queue_ends )
|
||||
*/
|
||||
TAILQ_ENTRY( rtems_can_queue_edge ) output_peers;
|
||||
/**
|
||||
* @brief This member holds the lists of peers FIFOs connected by their
|
||||
* output side ( @ref output_ends ) to the same terminal
|
||||
* ( @ref rtems_can_queue_ends ) with same priority and active state.
|
||||
*/
|
||||
TAILQ_ENTRY( rtems_can_queue_edge ) activepeers;
|
||||
/**
|
||||
* @brief This member holds the pointer to the activepeers head.
|
||||
*/
|
||||
struct rtems_can_queue_edges_list *peershead;
|
||||
/**
|
||||
* @brief This member holds the pointer to the FIFO input side terminal
|
||||
* ( @ref rtems_can_queue_ends ).
|
||||
*/
|
||||
struct rtems_can_queue_ends *input_ends;
|
||||
/**
|
||||
* @brief This member holds the pointer to the FIFO output side terminal
|
||||
* ( @ref rtems_can_queue_ends ).
|
||||
*/
|
||||
struct rtems_can_queue_ends *output_ends;
|
||||
/**
|
||||
* @brief This member holds the atomic usage counter, mainly used for
|
||||
* safe destruction of the edge.
|
||||
*/
|
||||
atomic_uint edge_used;
|
||||
/**
|
||||
* @brief This member holds the the assigned queue priority from the
|
||||
* range 0 to @ref RTEMS_CAN_QUEUE_PRIO_NR - 1
|
||||
*/
|
||||
int edge_prio;
|
||||
/**
|
||||
* @brief This member holds the edge sequential number intended for
|
||||
* debugging purposes only.
|
||||
*/
|
||||
int edge_num;
|
||||
};
|
||||
|
||||
struct rtems_can_queue_ends;
|
||||
TAILQ_HEAD( rtems_can_queue_ends_list, rtems_can_queue_ends );
|
||||
|
||||
/**
|
||||
* @brief This structure represents place to connect edges to for CAN
|
||||
* communication entity. The zero, one or more incoming and outgoing edges
|
||||
* can be connected to this structure.
|
||||
*/
|
||||
struct rtems_can_queue_ends {
|
||||
/**
|
||||
* @brief This member holds the array of the lists of active edges
|
||||
* directed to the ends structure.
|
||||
*/
|
||||
struct rtems_can_queue_edges_list active[RTEMS_CAN_QUEUE_PRIO_NR];
|
||||
/**
|
||||
* @brief This member holds the list of the edges directed to the ends
|
||||
* structure with empty FIFOs.
|
||||
*/
|
||||
struct rtems_can_queue_edges_list idle;
|
||||
/**
|
||||
* @brief This member holds the list of outgoing edges input sides.
|
||||
*/
|
||||
struct rtems_can_queue_edges_list inlist;
|
||||
/**
|
||||
* @brief This member holds the list of all incoming edges output sides.
|
||||
* Each of there edges is listed on one of @ref active or @ref idle lists.
|
||||
*/
|
||||
struct rtems_can_queue_edges_list outlist;
|
||||
/**
|
||||
* @brief This member holds the lock synchronizing operations between
|
||||
* threads accessing the ends.
|
||||
*/
|
||||
rtems_mutex ends_lock;
|
||||
/**
|
||||
* @brief pointer to notify procedure. The next state changes are notified.
|
||||
*
|
||||
* @ref RTEMS_CAN_QUEUE_NOTIFY_EMPTY (out->in call) - all slots are
|
||||
* processed by FIFO out side.
|
||||
*
|
||||
* @ref RTEMS_CAN_QUEUE_NOTIFY_SPACE (out->in call) - full state negated
|
||||
* => there is space for new message.
|
||||
*
|
||||
* @ref RTEMS_CAN_QUEUE_NOTIFY_PROC (in->out call) - empty state negated
|
||||
* => out side is requested to process slots.
|
||||
*
|
||||
* @ref RTEMS_CAN_QUEUE_NOTIFY_NOUSR (both) - notify, that the last user
|
||||
* has released the edge usage called with some lock to prevent edge
|
||||
* disappear.
|
||||
*
|
||||
* @ref RTEMS_CAN_QUEUE_NOTIFY_DEAD (both) - edge is in progress of
|
||||
* deletion.
|
||||
*
|
||||
* @ref RTEMS_CAN_QUEUE_NOTIFY_ATTACH (both) - new edge has been attached
|
||||
* to end.
|
||||
*
|
||||
* @param qends The pointer to @ref rtems_can_queue_ends structure.
|
||||
* @param qedge The pointer to @ref rtems_can_queue_edge structure.
|
||||
* @param what Integer specifying notification changes.
|
||||
*/
|
||||
void (*notify)(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge *qedge,
|
||||
int what
|
||||
);
|
||||
/**
|
||||
* @brief This member is used to chain ends wanting for postponed
|
||||
* destruction.
|
||||
*/
|
||||
TAILQ_ENTRY( rtems_can_queue_ends ) dead_peers;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief out-> in all slots are processed by FIFO out side.
|
||||
*/
|
||||
#define RTEMS_CAN_QUEUE_NOTIFY_EMPTY ( 1 )
|
||||
/**
|
||||
* @brief out -> in - full state negated => there is space for new message.
|
||||
*/
|
||||
#define RTEMS_CAN_QUEUE_NOTIFY_SPACE ( 2 )
|
||||
/**
|
||||
* @brief out -> in - full state negated => there is space for new message.
|
||||
*/
|
||||
#define RTEMS_CAN_QUEUE_NOTIFY_PROC ( 3 )
|
||||
/**
|
||||
* @brief Notify, that the last user has released the edge usage called
|
||||
* with some lock to prevent edge disappear.
|
||||
*/
|
||||
#define RTEMS_CAN_QUEUE_NOTIFY_NOUSR ( 4 )
|
||||
/**
|
||||
* @brief Edge is in progress of deletion.
|
||||
*/
|
||||
#define RTEMS_CAN_QUEUE_NOTIFY_DEAD ( 5 )
|
||||
/**
|
||||
* @brief Edge should be deleted.
|
||||
*/
|
||||
#define RTEMS_CAN_QUEUE_NOTIFY_DEAD_WANTED ( 6 )
|
||||
/**
|
||||
* @brief Edge is in progress of deletion.
|
||||
*/
|
||||
#define RTEMS_CAN_QUEUE_NOTIFY_ATTACH ( 7 )
|
||||
|
||||
/**
|
||||
* @brief This function finds one outgoing edge and allocates slot from it.
|
||||
*
|
||||
* Function looks for the first non-blocked outgoing edge in @qends structure
|
||||
* and tries to allocate slot from it.
|
||||
*
|
||||
* @param qends Ends structure belonging to calling communication object.
|
||||
* @param qedgep Place to store pointer to found edge.
|
||||
* @param slotp Place to store pointer to allocated slot.
|
||||
* @param cmd Command type for slot.
|
||||
*
|
||||
* @return If there is no usable edge or there is no free slot in edge
|
||||
* negative value is returned.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_get_inslot(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge **qedgep,
|
||||
struct rtems_can_queue_slot **slotp,
|
||||
int cmd
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief This function finds best outgoing edge and slot for given priority.
|
||||
*
|
||||
* Function looks for the non-blocked outgoing edge accepting messages
|
||||
* with given ID. If edge is found, slot is allocated from that edge.
|
||||
* The edges with non-zero mask are preferred over edges open to all messages.
|
||||
* If more edges with mask accepts given message ID, the edge with
|
||||
* highest priority below or equal to required priority is selected.
|
||||
*
|
||||
* @param qends Ends structure belonging to calling communication
|
||||
* object.
|
||||
* @param qedgep Place to store pointer to found edge.
|
||||
* @param slotp Place to store pointer to allocated slot.
|
||||
* @param cmd Command type for slot.
|
||||
* @param can_frame_header Pointer to CAN frame header.
|
||||
* @param prio Optional priority of message,
|
||||
*
|
||||
* @return If there is no usable edge or there is no free slot in edge
|
||||
* negative value is returned.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_get_inslot_for_prio(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge **qedgep,
|
||||
struct rtems_can_queue_slot **slotp,
|
||||
const struct can_frame_header *header,
|
||||
int cmd,
|
||||
int prio
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief This function tests whether there is a free space in any outgoing
|
||||
* edge.
|
||||
*
|
||||
* Function looks for the first non-blocked outgoing edge in @qends structure
|
||||
* with free space for slot.
|
||||
*
|
||||
* @param qends Ends structure belonging to calling communication object.
|
||||
*
|
||||
* @return 0 if there is usable edge with free space available, -1
|
||||
* otherwise.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_test_inslot( struct rtems_can_queue_ends *qends );
|
||||
|
||||
/**
|
||||
* @brief This function schedules filled slot for processing
|
||||
*
|
||||
* Puts slot previously acquired by @ref rtems_can_queue_get_inslot or
|
||||
* @ref rtems_can_queue_get_inslot_for_prio function call into FIFO queue and
|
||||
* activates edge processing if needed.
|
||||
*
|
||||
* @param qends Ends structure belonging to calling communication object.
|
||||
* @param qedge Edge the slot belongs to.
|
||||
* @param slot Pointer to the preprared slot.
|
||||
*
|
||||
* @return Positive value informs, that activation of output end
|
||||
* has been necessary
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_put_inslot(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge *qedge,
|
||||
struct rtems_can_queue_slot *slot
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief This function aborts preparation of the message in the slot
|
||||
*
|
||||
* Frees slot previously acquired by @ref rtems_can_queue_get_inslot or
|
||||
* @ref rtems_can_queue_get_inslot_for_prio function call. Used when message
|
||||
* copying into slot fails.
|
||||
*
|
||||
* @param qends Ends structure belonging to calling communication object.
|
||||
* @param qedge Edge the slot belongs to.
|
||||
* @param slot Pointer to the preprared slot.
|
||||
*
|
||||
* @return Positive value informs, that queue full state has been negated.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_abort_inslot(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge *qedge,
|
||||
struct rtems_can_queue_slot *slot
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief This function sends message into all edges which accept its ID.
|
||||
*
|
||||
* Sends message to all outgoing edges connected to the given ends, which
|
||||
* accepts message communication ID.
|
||||
*
|
||||
* @param qends Ends structure belonging to calling communication object
|
||||
* @param src_edge Optional source edge for echo detection
|
||||
* @param frame Pointer to CAN frame.
|
||||
* @param flags2add Optional additional CAN Frame flags.
|
||||
*
|
||||
* @return Returns number of edges message has been send to.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_filter_frame_to_edges(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge *src_edge,
|
||||
struct can_frame *frame,
|
||||
unsigned int flags2add
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief This function tests and retrieves ready slot for given ends.
|
||||
*
|
||||
* Function takes highest priority active incoming edge and retrieves
|
||||
* oldest ready slot from it.
|
||||
*
|
||||
* @param qends Ends structure belonging to calling communication object.
|
||||
* @param qedgep Place to store pointer to found edge.
|
||||
* @param slotp Place to store pointer to received slot.
|
||||
*
|
||||
* @return Negative value informs, that there is no ready output
|
||||
* slot for given ends. Positive value is equal to the command
|
||||
* slot has been allocated by the input side.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_test_outslot(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge **qedgep,
|
||||
struct rtems_can_queue_slot **slotp
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief This function tests ready outslot with minimum priority
|
||||
*
|
||||
* Function searches for ready slot in active incoming edge. The difference
|
||||
* from @ref rtems_can_queue_test_outslot function is that this function does
|
||||
* not retreive the slot from FIFO, it just checks its existence. This can be
|
||||
* used to determined whether there is a slot with higher priority class
|
||||
* in the infrastruce.
|
||||
*
|
||||
* @param qends Ends structure belonging to calling communication object.
|
||||
* @param prio_min Minimum slot priority to be considered
|
||||
*
|
||||
* @return Negative value informs, that there is no ready output
|
||||
* slot for given ends and minimum priority. Positive value informs about
|
||||
* the available slot priority.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_pending_outslot_prio(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
int prio_min
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief This function frees processed output slot.
|
||||
*
|
||||
* Function releases processed slot previously acquired by
|
||||
* @ref rtems_can_queue_test_outslot function call.
|
||||
*
|
||||
* @param qends Ends structure belonging to calling communication object.
|
||||
* @param qedge Edge the slot belongs to.
|
||||
* @param slot Pointer to the processed slot.
|
||||
*
|
||||
* @return Informs if input side has been notified
|
||||
* to know about change of edge state
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_free_outslot(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge *qedge,
|
||||
struct rtems_can_queue_slot *slot
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief This function reschedules output slot to process it again later.
|
||||
*
|
||||
* Function reschedules slot previously acquired by
|
||||
* @ref rtems_can_queue_test_outslot function call for second time processing.
|
||||
*
|
||||
* @param qends Ends structure belonging to calling communication object.
|
||||
* @param qedge Edge the slot belongs to.
|
||||
* @param slot Pointer to the processed slot.
|
||||
*
|
||||
* @return Function cannot fail.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_push_back_outslot(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge *qedge,
|
||||
struct rtems_can_queue_slot *slot
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief This function flushes all ready slots in the edge.
|
||||
*
|
||||
* Tries to flush all allocated slots from the edge, but there could
|
||||
* exist some slots associated to edge which are processed by input
|
||||
* or output side and cannot be flushed at this moment
|
||||
*
|
||||
* @param qedge Pointer to the edge.
|
||||
*
|
||||
* @return The nonzero value indicates, that queue has not been
|
||||
* empty before the function call
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_flush( struct rtems_can_queue_edge *qedge );
|
||||
|
||||
/**
|
||||
* @brief This function disconnects edge from communication entities.
|
||||
*
|
||||
* @param qedge Pointer to edge.
|
||||
*
|
||||
* @return Negative value means, that edge is used by somebody
|
||||
* other and cannot be disconnected. Operation has to be delayed.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_disconnect_edge( struct rtems_can_queue_edge *qedge );
|
||||
|
||||
/**
|
||||
* @brief This function connects edge between two communication entities.
|
||||
*
|
||||
* @param qedge Pointer to edge.
|
||||
* @param input_ends Pointer to ends the input of the edge should be
|
||||
* connected to.
|
||||
* @param output_ends Pointer to ends the output of the edge should be
|
||||
* connected to.
|
||||
*
|
||||
* @return Negative value informs about failed operation.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_connect_edge(
|
||||
struct rtems_can_queue_edge *qedge,
|
||||
struct rtems_can_queue_ends *input_ends,
|
||||
struct rtems_can_queue_ends *output_ends
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief This function implements subsystem independent routine to initialize
|
||||
* ends state.
|
||||
*
|
||||
* @param qends Pointer to the end.
|
||||
*
|
||||
* @return This fucntion cannot fail.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_ends_init( struct rtems_can_queue_ends *qends );
|
||||
|
||||
/**
|
||||
* @brief This function blocks slot allocation of all outgoing edges of
|
||||
* specified ends
|
||||
*
|
||||
* @param qends Pointer to ends structure.
|
||||
*
|
||||
* @return None.
|
||||
*
|
||||
*/
|
||||
void rtems_can_queue_block_inlist( struct rtems_can_queue_ends *qends );
|
||||
|
||||
/**
|
||||
* @brief This function blocks slot allocation of all incoming edges of
|
||||
* specified ends
|
||||
*
|
||||
* @param qends Pointer to ends structure.
|
||||
*
|
||||
* @return None.
|
||||
*
|
||||
*/
|
||||
void rtems_can_queue_block_outlist( struct rtems_can_queue_ends *qends );
|
||||
|
||||
/**
|
||||
* @brief This function sends request to die to all outgoing edges
|
||||
*
|
||||
* @param qends Pointer to ends structure.
|
||||
* @param send_rest Select, whether already allocated slots should be
|
||||
* processed by FIFO output side.
|
||||
*
|
||||
* @return Non-zero value means, that not all edges could be immediately
|
||||
* disconnected and that ends structure memory release has to be delayed.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_ends_kill_inlist(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
int send_rest
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief This function sends request to die to all incomming edges
|
||||
*
|
||||
* @param qends Pointer to ends structure.
|
||||
*
|
||||
* @return Non-zero value means, that not all edges could be immediately
|
||||
* disconnected and that ends structure memory release has to be delayed.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_ends_kill_outlist( struct rtems_can_queue_ends *qends );
|
||||
|
||||
/**
|
||||
* @brief This function flushes all messages in incoming edges
|
||||
*
|
||||
* @param qends: pointer to ends structure
|
||||
*
|
||||
* Return Value: Negative value informs about unsuccessful result
|
||||
*/
|
||||
int rtems_can_queue_ends_flush_inlist( struct rtems_can_queue_ends *qends );
|
||||
|
||||
/**
|
||||
* @brief This function flushes all messages in outgoing edges
|
||||
*
|
||||
* @param qends Pointer to ends structure.
|
||||
*
|
||||
* @return Negative value informs about unsuccessful result.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_ends_flush_outlist( struct rtems_can_queue_ends *qends );
|
||||
|
||||
/**
|
||||
* @brief This function decrements the edge user counter
|
||||
*
|
||||
* @param qedge Pointer to the edge to be disconnected.
|
||||
*
|
||||
* @return None
|
||||
*
|
||||
*/
|
||||
void rtems_can_queue_do_edge_decref( struct rtems_can_queue_edge *edge );
|
||||
|
||||
/**
|
||||
* @brief This function disconnects the edge
|
||||
*
|
||||
* @param qedge Pointer to the edge to be disconnected.
|
||||
*
|
||||
* @return None
|
||||
*
|
||||
*/
|
||||
void rtems_can_queue_edge_do_dead( struct rtems_can_queue_edge *qedge );
|
||||
|
||||
/**
|
||||
* @brief This function initializes one CAN FIFO.
|
||||
*
|
||||
* @param fifo Pointer to the FIFO structure.
|
||||
* @param allocated_slot_count Number of requested slots.
|
||||
* @param max_data_length Maximum size of data in one slot/frame.
|
||||
*
|
||||
* @return The negative value indicates, that there is no memory
|
||||
* to allocate space for the requested number of the slots.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_fifo_init_kern(
|
||||
struct rtems_can_queue_fifo *fifo,
|
||||
int allocated_slot_count,
|
||||
int max_data_length
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief This function frees slots allocated for CAN FIFO.
|
||||
*
|
||||
* @param fifo Pointer to the FIFO structure.
|
||||
*
|
||||
* @return This function should not fail.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_fifo_done_kern( struct rtems_can_queue_fifo *fifo );
|
||||
|
||||
/**
|
||||
* @brief This function allocates new edge structure.
|
||||
*
|
||||
* @param allocated_slot_count Required number of slots in the newly allocated
|
||||
* edge structure.
|
||||
* @param max_data_length Maximul data length of one CAN frame.
|
||||
*
|
||||
* @return Pointer to rtems_can_queue_edge structure on success, NULL on error.
|
||||
*
|
||||
*/
|
||||
struct rtems_can_queue_edge *rtems_can_queue_new_edge_kern(
|
||||
int allocated_slot_countm,
|
||||
int max_data_length
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief This function waits for all ends to TX their messages.
|
||||
*
|
||||
* @param qends Pointer to ends structure.
|
||||
* @param ts Absolute time againts CLOCK_MONOTONIC that informs
|
||||
* rtems_can_queue_sync_wait_kern how long to wait.
|
||||
*
|
||||
* @return 0 on success, -ETIME in case of timeout.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_ends_sync_all_kern(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct timespec *ts
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief This function waits for all slots processing
|
||||
*
|
||||
* @param qends Ends structure belonging to calling communication object.
|
||||
* @param qedge Pointer to edge.
|
||||
* @param nowait True if semaphore should not wait
|
||||
* @param timeout Number of clock ticks to wait for semaphore. Passing zero
|
||||
* indicates an infinite timeout.
|
||||
*
|
||||
* @return Positive value indicates, that edge empty state has been reached.
|
||||
* Negative or zero value informs the semaphore timeouted.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_sync_wait_kern(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
struct rtems_can_queue_edge *qedge,
|
||||
bool nowait,
|
||||
rtems_interval timeout
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief This function provides finalizing of the ends structure for clients.
|
||||
*
|
||||
* @param qends Pointer to ends structure.
|
||||
* @param nonblock Flag indicating that user does not want to wait for
|
||||
* processing of all remaining messages.
|
||||
*
|
||||
* @return Function should be designed such way to not fail.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_ends_dispose_kern(
|
||||
struct rtems_can_queue_ends *qends,
|
||||
bool nonblock
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief This function provides initialization of kernel queue side.
|
||||
*
|
||||
* @return Zero on success.
|
||||
*
|
||||
*/
|
||||
int rtems_can_queue_kern_initialize( void );
|
||||
|
||||
#endif /* __DEV_CAN_CAN_QUEUE_H */
|
||||
194
cpukit/include/dev/can/can-stats.h
Normal file
194
cpukit/include/dev/can/can-stats.h
Normal file
@@ -0,0 +1,194 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup CANFDStack
|
||||
*
|
||||
* @brief This header file is part of CAN/CAN FD bus common support. It
|
||||
* implements controller's statistics.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
|
||||
* Implementation is based on original LinCAN - Linux CAN bus driver
|
||||
* Part of OrtCAN project https://ortcan.sourceforge.net/
|
||||
* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://control.fel.cvut.cz>
|
||||
* Copyright (C) 2002-2024 Pavel Pisa <pisa@cmp.felk.cvut.cz>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _DEV_CAN_CAN_STATS_H
|
||||
#define _DEV_CAN_CAN_STATS_H
|
||||
|
||||
/**
|
||||
* @brief This enum represents the current state of CAN controller
|
||||
*/
|
||||
enum can_state {
|
||||
/**
|
||||
* @brief This member indicates the controller is in error active state (
|
||||
* RX/TX error count < 96)
|
||||
*/
|
||||
CAN_STATE_ERROR_ACTIVE = 0,
|
||||
/**
|
||||
* @brief This member indicates the controller is in error warning state (
|
||||
* RX/TX error count < 128)
|
||||
*/
|
||||
CAN_STATE_ERROR_WARNING,
|
||||
/**
|
||||
* @brief This member indicates the controller is in error passive state (
|
||||
* RX/TX error count < 256)
|
||||
*/
|
||||
CAN_STATE_ERROR_PASSIVE,
|
||||
/**
|
||||
* @brief This member indicates the controller is in bus off state (
|
||||
* RX/TX error count >= 256)
|
||||
*/
|
||||
CAN_STATE_BUS_OFF,
|
||||
/**
|
||||
* @brief This member indicates the the controller is stopped.
|
||||
*/
|
||||
CAN_STATE_STOPPED,
|
||||
/**
|
||||
* @brief This member indicates the the controller is in sleep.
|
||||
*/
|
||||
CAN_STATE_SLEEPING,
|
||||
/**
|
||||
* @brief This member indicates the the controller is in stopping process.
|
||||
*/
|
||||
CAN_STATE_STOPPING,
|
||||
/**
|
||||
* @brief This member holds the maximum number of controller's states.
|
||||
*/
|
||||
CAN_STATE_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This structure is used to represent CAN statistics
|
||||
*/
|
||||
struct rtems_can_stats {
|
||||
/**
|
||||
* @brief This member holds number of succesful TX frames.
|
||||
*/
|
||||
unsigned long tx_done;
|
||||
/**
|
||||
* @brief This member holds number of succesful RX frames.
|
||||
*/
|
||||
unsigned long rx_done;
|
||||
/**
|
||||
* @brief This member holds number of bytes succesfully send.
|
||||
*/
|
||||
unsigned long tx_bytes;
|
||||
/**
|
||||
* @brief This member holds number of bytes succesfully received.
|
||||
*/
|
||||
unsigned long rx_bytes;
|
||||
/**
|
||||
* @brief This member holds number of TX errors.
|
||||
*/
|
||||
unsigned long tx_error;
|
||||
/**
|
||||
* @brief This member holds number of RX errors.
|
||||
*/
|
||||
unsigned long rx_error;
|
||||
/**
|
||||
* @brief This member holds number of overflows on RX side.
|
||||
*/
|
||||
unsigned long rx_overflows;
|
||||
/**
|
||||
* @brief This member holds controller's state (error active, passive,
|
||||
* bus off)
|
||||
*/
|
||||
int chip_state;
|
||||
};
|
||||
|
||||
static inline void rtems_can_stats_add_tx_done(
|
||||
struct rtems_can_stats *stats
|
||||
)
|
||||
{
|
||||
stats->tx_done += 1;
|
||||
}
|
||||
|
||||
static inline void rtems_can_stats_add_rx_done(
|
||||
struct rtems_can_stats *stats
|
||||
)
|
||||
{
|
||||
stats->rx_done += 1;
|
||||
}
|
||||
|
||||
static inline void rtems_can_stats_add_tx_bytes(
|
||||
struct rtems_can_stats *stats,
|
||||
uint16_t nbytes
|
||||
)
|
||||
{
|
||||
stats->tx_bytes += nbytes;
|
||||
}
|
||||
|
||||
static inline void rtems_can_stats_add_rx_bytes(
|
||||
struct rtems_can_stats *stats,
|
||||
uint16_t nbytes
|
||||
)
|
||||
{
|
||||
stats->rx_bytes += nbytes;
|
||||
}
|
||||
|
||||
static inline void rtems_can_stats_add_tx_error(
|
||||
struct rtems_can_stats *stats
|
||||
)
|
||||
{
|
||||
stats->tx_error += 1;
|
||||
}
|
||||
|
||||
static inline void rtems_can_stats_add_rx_error(
|
||||
struct rtems_can_stats *stats
|
||||
)
|
||||
{
|
||||
stats->rx_error += 1;
|
||||
}
|
||||
|
||||
static inline void rtems_can_stats_add_rx_overflows(
|
||||
struct rtems_can_stats *stats
|
||||
)
|
||||
{
|
||||
stats->rx_overflows += 1;
|
||||
}
|
||||
|
||||
static inline void rtems_can_stats_set_state(
|
||||
struct rtems_can_stats *stats,
|
||||
enum can_state state
|
||||
)
|
||||
{
|
||||
stats->chip_state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function resets the controller's statistics.
|
||||
*
|
||||
* This sets all fields of @ref rtems_can_stats structure to zero. It
|
||||
* should be called by the controller after it is started.
|
||||
*
|
||||
* @param stats Pointer to @ref rtems_can_stats structure.
|
||||
*/
|
||||
void rtems_can_stats_reset( struct rtems_can_stats *stats );
|
||||
|
||||
#endif /* _DEV_CAN_CAN_STATS_H */
|
||||
380
cpukit/include/dev/can/can.h
Normal file
380
cpukit/include/dev/can/can.h
Normal file
@@ -0,0 +1,380 @@
|
||||
/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* @ingroup CANFDStack
|
||||
*
|
||||
* @brief This file is part of CAN/CAN FD bus common support
|
||||
* and defines generic CAN structures used for common IO
|
||||
* operations.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
|
||||
* Implementation is based on original LinCAN - Linux CAN bus driver
|
||||
* Part of OrtCAN project https://ortcan.sourceforge.net/
|
||||
* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://control.fel.cvut.cz>
|
||||
* Copyright (C) 2002-2024 Pavel Pisa <pisa@cmp.felk.cvut.cz>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _DEV_CAN_CAN_H
|
||||
#define _DEV_CAN_CAN_H
|
||||
|
||||
#include <sys/queue.h>
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <rtems.h>
|
||||
#include <rtems/seterr.h>
|
||||
#include <rtems/thread.h>
|
||||
#include <sys/ioccom.h>
|
||||
|
||||
#include <dev/can/can-stats.h>
|
||||
#include <dev/can/can-bittiming.h>
|
||||
#include <dev/can/can-frame.h>
|
||||
#include <dev/can/can-filter.h>
|
||||
|
||||
#define CAN_IOC_MAGIC 'd'
|
||||
|
||||
/**
|
||||
* @defgroup CANChip
|
||||
*
|
||||
* @ingroup CANFDStack
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name RTEMS CAN Chip Flags
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief This define provides controller flags determining whether the chip
|
||||
* is configured and ready to be started.
|
||||
*/
|
||||
#define RTEMS_CAN_CHIP_CONFIGURED ( 0 )
|
||||
/**
|
||||
* @brief This define provides controller flags determining whether the chip
|
||||
* is currently running or not.
|
||||
*/
|
||||
#define RTEMS_CAN_CHIP_RUNNING ( 1 )
|
||||
|
||||
/**
|
||||
* @name RTEMS CAN Chip Capabilities
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief This define provides controller flags determining whether the chip
|
||||
* is CAN FD capable.
|
||||
*/
|
||||
#define RTEMS_CAN_CHIP_CAPABILITIES_FD ( 1 << 0 )
|
||||
/**
|
||||
* @brief This define provides controller flags determining whether the chip
|
||||
* has TX timestamping support.
|
||||
*/
|
||||
#define RTEMS_CAN_CHIP_CAPABILITIES_TX_TIMESTAMP ( 1 << 1 )
|
||||
/**
|
||||
* @brief This define provides controller flags determining whether the chip
|
||||
* has TX timestamping support.
|
||||
*/
|
||||
#define RTEMS_CAN_CHIP_CAPABILITIES_RX_TIMESTAMP ( 1 << 2 )
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name RTEMS CAN Chip Info.
|
||||
*
|
||||
* Arguments that can be used with @ref RTEMS_CAN_CHIP_GET_INFO IOCTL command.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief This define specifies user wants to obtain controller's nominal
|
||||
* bitrate via @ref RTEMS_CAN_CHIP_GET_INFO ioctl.
|
||||
*/
|
||||
#define RTEMS_CAN_CHIP_BITRATE ( 1 )
|
||||
/**
|
||||
* @brief This define specifies user wants to obtain controller's data bitrate
|
||||
* via @ref RTEMS_CAN_CHIP_GET_INFO ioctl.
|
||||
*/
|
||||
#define RTEMS_CAN_CHIP_DBITRATE ( 2 )
|
||||
/**
|
||||
* @brief This define specifies user wants to obtain number of users using
|
||||
* the controller via @ref RTEMS_CAN_CHIP_GET_INFO ioctl.
|
||||
*/
|
||||
#define RTEMS_CAN_CHIP_NUSERS ( 3 )
|
||||
/**
|
||||
* @brief This define specifies user wants to obtain controller's flags
|
||||
* via @ref RTEMS_CAN_CHIP_GET_INFO ioctl. Refer to @ref CANChip
|
||||
* for flags definitions.
|
||||
*/
|
||||
#define RTEMS_CAN_CHIP_FLAGS ( 4 )
|
||||
/**
|
||||
* @brief This define specifies user wants to obtain currently set
|
||||
* controller's modes via @ref RTEMS_CAN_CHIP_GET_INFO ioctl.
|
||||
*/
|
||||
#define RTEMS_CAN_CHIP_MODE ( 5 )
|
||||
/**
|
||||
* @brief This define specifies user wants to obtain modes supported
|
||||
* by the controller via @ref RTEMS_CAN_CHIP_GET_INFO ioctl.
|
||||
*/
|
||||
#define RTEMS_CAN_CHIP_MODE_SUPPORTED ( 6 )
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name RTEMS CAN Queue Direction Helpers
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief This define can be used to select queue RX (from controller to
|
||||
* application) direction.
|
||||
*/
|
||||
#define RTEMS_CAN_QUEUE_RX ( 1 << 0 )
|
||||
/**
|
||||
* @brief This define can be used to select queue TX (from application to
|
||||
* controller) direction.
|
||||
*/
|
||||
#define RTEMS_CAN_QUEUE_TX ( 1 << 1 )
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name RTEMS CAN Modes
|
||||
*
|
||||
* These can be set with @ref RTEMS_CAN_CHIP_SET_MODE command.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief This define is used to set loopback mode via
|
||||
* @ref RTEMS_CAN_CHIP_SET_MODE ioctl.
|
||||
*/
|
||||
#define CAN_CTRLMODE_LOOPBACK ( 1 << 0 )
|
||||
/**
|
||||
* @brief This define is used to set listen only mode via
|
||||
* @ref RTEMS_CAN_CHIP_SET_MODE ioctl.
|
||||
*/
|
||||
#define CAN_CTRLMODE_LISTENONLY ( 1 << 1 )
|
||||
/**
|
||||
* @brief This define is used to set triple sampling mode via
|
||||
* @ref RTEMS_CAN_CHIP_SET_MODE ioctl.
|
||||
*/
|
||||
#define CAN_CTRLMODE_3_SAMPLES ( 1 << 2 )
|
||||
/**
|
||||
* @brief This define is used to set one shot mode via
|
||||
* @ref RTEMS_CAN_CHIP_SET_MODE ioctl.
|
||||
*/
|
||||
#define CAN_CTRLMODE_ONE_SHOT ( 1 << 3 )
|
||||
/**
|
||||
* @brief This define is used to enable bus error reporting via
|
||||
* @ref RTEMS_CAN_CHIP_SET_MODE ioctl.
|
||||
*/
|
||||
#define CAN_CTRLMODE_BERR_REPORTING ( 1 << 4 )
|
||||
/**
|
||||
* @brief This define is used to set CAN FD mode via
|
||||
* @ref RTEMS_CAN_CHIP_SET_MODE ioctl.
|
||||
*/
|
||||
#define CAN_CTRLMODE_FD ( 1 << 5 )
|
||||
/**
|
||||
* @brief This define is used to set to ignore missing CAN ack via
|
||||
* @ref RTEMS_CAN_CHIP_SET_MODE ioctl.
|
||||
*/
|
||||
#define CAN_CTRLMODE_PRESUME_ACK ( 1 << 6 )
|
||||
/**
|
||||
* @brief This define is used to set CAN FD in non-ISO mode via
|
||||
* @ref RTEMS_CAN_CHIP_SET_MODE ioctl.
|
||||
*/
|
||||
#define CAN_CTRLMODE_FD_NON_ISO ( 1 << 7 )
|
||||
/**
|
||||
* @brief This define is used to set classic CAN DLC option via
|
||||
* @ref RTEMS_CAN_CHIP_SET_MODE ioctl.
|
||||
*/
|
||||
#define CAN_CTRLMODE_CC_LEN8_DLC ( 1 << 8 )
|
||||
/**
|
||||
* @brief This define is used to let CAN transiver automatically calculates TDCV
|
||||
* via @ref RTEMS_CAN_CHIP_SET_MODE ioctl.
|
||||
*/
|
||||
#define CAN_CTRLMODE_TDC_AUTO ( 1 << 9 )
|
||||
/**
|
||||
* @brief This define is used to let TDCV calculate manually by user
|
||||
* via @ref RTEMS_CAN_CHIP_SET_MODE ioctl.
|
||||
*/
|
||||
#define CAN_CTRLMODE_TDC_MANUAL ( 1 << 10 )
|
||||
#define CAN_CTRLMODE_MASK ( 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 | \
|
||||
CAN_CTRLMODE_TDC_MANUAL)
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup CANIoctl
|
||||
*
|
||||
* @ingroup CANFDStack
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name RTEMS CAN Stack Supported IOCTL calls.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief This ioctl call starts the controller. This is required in order
|
||||
* to perform read/write operations. It has no effect if the chip is already
|
||||
* started.
|
||||
*/
|
||||
#define RTEMS_CAN_CHIP_START _IO( CAN_IOC_MAGIC, 1 )
|
||||
/**
|
||||
* @brief This ioctl call stops the controller. It has no effect if the chip
|
||||
* is already stopped.
|
||||
*/
|
||||
#define RTEMS_CAN_CHIP_STOP _IOW( CAN_IOC_MAGIC, 2, struct timespec )
|
||||
/**
|
||||
* @brief This ioctl call sets close operation to be blocking or nonblocking
|
||||
* based on input parameter.
|
||||
*/
|
||||
#define RTEMS_CAN_CLOSE_NONBLOCK _IO( CAN_IOC_MAGIC, 3 )
|
||||
/**
|
||||
* @brief This ioctl call discards all RX/TX queues. Direction can be
|
||||
* set with uint8_t parameter, see @ref RTEMS_CAN_QUEUE_RX and
|
||||
* @ref RTEMS_CAN_QUEUE_TX.
|
||||
*/
|
||||
#define RTEMS_CAN_DISCARD_QUEUES _IO( CAN_IOC_MAGIC, 4 )
|
||||
/**
|
||||
* @brief This ioctl call flushes all RX/TX queues. Direction can be
|
||||
* set with uint8_t parameter, see @ref RTEMS_CAN_QUEUE_RX and
|
||||
* @ref RTEMS_CAN_QUEUE_TX.
|
||||
*/
|
||||
#define RTEMS_CAN_FLUSH_QUEUES _IO( CAN_IOC_MAGIC, 5 )
|
||||
/**
|
||||
* @brief This ioctl call sets controller's mode. Modes are
|
||||
* passed uint32_t argument. Writing mode unsupported by the controller
|
||||
* results in error as well as setting mode when the chip is already started.
|
||||
*/
|
||||
#define RTEMS_CAN_CHIP_SET_MODE _IO( CAN_IOC_MAGIC, 6 )
|
||||
/**
|
||||
* @brief This ioctl call obtains controller's info specified by input integer
|
||||
* argument
|
||||
*/
|
||||
#define RTEMS_CAN_CHIP_GET_INFO _IO( CAN_IOC_MAGIC, 7 )
|
||||
/**
|
||||
* @brief This ioctl call waits until all TX messages from all FIFOs are
|
||||
* sent to the network or until timeout defined in struct timespec. The
|
||||
* timeout is specified as a relative timeout. Passing NULL indicates
|
||||
* an infinite timeout.
|
||||
*/
|
||||
#define RTEMS_CAN_WAIT_TX_DONE _IOW( CAN_IOC_MAGIC, 8, struct timespec )
|
||||
/**
|
||||
* @brief This ioctl call waits until there is a free space in any TX
|
||||
* FIFO queue or until timeout defined in struct timespec. The
|
||||
* timeout is specified as a relative timeout. Passing NULL indicates
|
||||
* an infinite timeout.
|
||||
*/
|
||||
#define RTEMS_CAN_POLL_TX_READY _IOW( CAN_IOC_MAGIC, 9, struct timespec )
|
||||
/**
|
||||
* @brief This ioctl call waits until there is an available frame in any RX
|
||||
* FIFO queue or until timeout defined in struct timespec. The
|
||||
* timeout is specified as a relative timeout. Passing NULL indicates
|
||||
* an infinite timeout.
|
||||
*/
|
||||
#define RTEMS_CAN_POLL_RX_AVAIL _IOW( CAN_IOC_MAGIC, 10, struct timespec )
|
||||
/**
|
||||
* @brief This ioctl call creates new queue. Parameters are set with
|
||||
* @ref rtems_can_queue_param structure.
|
||||
*/
|
||||
#define RTEMS_CAN_CREATE_QUEUE _IOW( CAN_IOC_MAGIC, 11, struct rtems_can_queue_param )
|
||||
/**
|
||||
* @brief This ioctl call sets controller's bitrate. See structure
|
||||
* @ref rtems_can_set_bittiming.
|
||||
*/
|
||||
#define RTEMS_CAN_SET_BITRATE _IOW( CAN_IOC_MAGIC, 12, struct rtems_can_set_bittiming )
|
||||
/**
|
||||
* @brief This ioctl call obtains 64 unsigned timestamp from the controller.
|
||||
*/
|
||||
#define RTEMS_CAN_CHIP_GET_TIMESTAMP _IOR( CAN_IOC_MAGIC, 13, uint64_t )
|
||||
/**
|
||||
* @brief This ioctl call obtains controller's statistics via
|
||||
* @ref rtems_can_stats.
|
||||
*/
|
||||
#define RTEMS_CAN_CHIP_STATISTICS _IOR( CAN_IOC_MAGIC, 14, struct rtems_can_stats )
|
||||
/**
|
||||
* @brief This ioctl call gets controller's bitrate. See structure
|
||||
* @ref rtems_can_set_bittiming.
|
||||
*/
|
||||
#define RTEMS_CAN_GET_BITTIMING _IOWR( CAN_IOC_MAGIC, 15, struct rtems_can_get_bittiming )
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief This structure represents parameters of FIFO queue. It is used to
|
||||
* setup new queues via @ref RTEMS_CAN_CREATE_QUEUE ioctl call.
|
||||
*/
|
||||
struct rtems_can_queue_param {
|
||||
/**
|
||||
* @brief This member specifies queue's direction. Use
|
||||
* @ref RTEMS_CAN_QUEUE_RX or @ref RTEMS_CAN_QUEUE_TX
|
||||
*/
|
||||
uint8_t direction;
|
||||
/**
|
||||
* @brief This member specifies queue's priority. Maximum priority value
|
||||
* is available from @ref RTEMS_CAN_QUEUE_PRIO_NR define. Higher number
|
||||
* means higher priority.
|
||||
*/
|
||||
uint8_t priority;
|
||||
/**
|
||||
* @brief This member specifies queue's maximum data length. Passing 0
|
||||
* applies default value: @ref CAN_FRAME_STANDARD_DLEN for standard
|
||||
* frames only and @ref CAN_FRAME_FD_DLEN for CAN FD capable chips.
|
||||
*/
|
||||
uint8_t dlen_max;
|
||||
/**
|
||||
* @brief This member specifies queue's buffer size. Passing 0 applies
|
||||
* default @ref RTEMS_CAN_FIFO_SIZE value.
|
||||
*/
|
||||
uint8_t buffer_size;
|
||||
/**
|
||||
* @brief This member holds a queue's filter. Refer to @ref rtems_can_filter
|
||||
* for more information.
|
||||
*/
|
||||
struct rtems_can_filter filter;
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif
|
||||
@@ -33,6 +33,10 @@ links:
|
||||
uid: optboothartid
|
||||
- role: build-dependency
|
||||
uid: optbuildlabel
|
||||
- role: build-dependency
|
||||
uid: optcanfifosize
|
||||
- role: build-dependency
|
||||
uid: optcanqueueprios
|
||||
- role: build-dependency
|
||||
uid: optdebug
|
||||
- role: build-dependency
|
||||
|
||||
@@ -35,6 +35,18 @@ install:
|
||||
- destination: ${BSP_INCLUDEDIR}/arpa
|
||||
source:
|
||||
- cpukit/include/arpa/ftp.h
|
||||
- destination: ${BSP_INCLUDEDIR}/dev/can
|
||||
source:
|
||||
- cpukit/include/dev/can/can-bittiming.h
|
||||
- cpukit/include/dev/can/can-bus.h
|
||||
- cpukit/include/dev/can/can-devcommon.h
|
||||
- cpukit/include/dev/can/can-filter.h
|
||||
- cpukit/include/dev/can/can-frame.h
|
||||
- cpukit/include/dev/can/can-helpers.h
|
||||
- cpukit/include/dev/can/can-impl.h
|
||||
- cpukit/include/dev/can/can-queue.h
|
||||
- cpukit/include/dev/can/can-stats.h
|
||||
- cpukit/include/dev/can/can.h
|
||||
- destination: ${BSP_INCLUDEDIR}/dev/i2c
|
||||
source:
|
||||
- cpukit/include/dev/i2c/eeprom.h
|
||||
@@ -531,6 +543,11 @@ source:
|
||||
- cpukit/crc/crc24q.c
|
||||
- cpukit/base64/base64-encode.c
|
||||
- cpukit/base64/base64-decode.c
|
||||
- cpukit/dev/can/can-bittiming.c
|
||||
- cpukit/dev/can/can-bus.c
|
||||
- cpukit/dev/can/can-devcommon.c
|
||||
- cpukit/dev/can/can-quekern.c
|
||||
- cpukit/dev/can/can-queue.c
|
||||
- cpukit/dev/flash/flashdev.c
|
||||
- cpukit/dev/i2c/eeprom.c
|
||||
- cpukit/dev/i2c/fpga-i2c-slave.c
|
||||
|
||||
17
spec/build/cpukit/optcanfifosize.yml
Normal file
17
spec/build/cpukit/optcanfifosize.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later
|
||||
actions:
|
||||
- get-integer: null
|
||||
- define: null
|
||||
build-type: option
|
||||
copyrights:
|
||||
- Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
|
||||
default:
|
||||
- enabled-by: true
|
||||
value: 64
|
||||
description: |
|
||||
Default size in CAN frames of FIFO queues.
|
||||
enabled-by: true
|
||||
format: '{}'
|
||||
links: []
|
||||
name: RTEMS_CAN_FIFO_SIZE
|
||||
type: build
|
||||
17
spec/build/cpukit/optcanqueueprios.yml
Normal file
17
spec/build/cpukit/optcanqueueprios.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later
|
||||
actions:
|
||||
- get-integer: null
|
||||
- define: null
|
||||
build-type: option
|
||||
copyrights:
|
||||
- Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
|
||||
default:
|
||||
- enabled-by: true
|
||||
value: 3
|
||||
description: |
|
||||
Number of available priorities for CAN priority queues.
|
||||
enabled-by: true
|
||||
format: '{}'
|
||||
links: []
|
||||
name: RTEMS_CAN_QUEUE_PRIO_NR
|
||||
type: build
|
||||
Reference in New Issue
Block a user