diff --git a/cpukit/dev/can/can-bittiming.c b/cpukit/dev/can/can-bittiming.c new file mode 100644 index 0000000000..35c9d57c66 --- /dev/null +++ b/cpukit/dev/can/can-bittiming.c @@ -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 + * 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 + * Copyright (C) 2002-2024 Pavel Pisa + * 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 +#include + +#include +#include + +#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; +} diff --git a/cpukit/dev/can/can-bus.c b/cpukit/dev/can/can-bus.c new file mode 100644 index 0000000000..144b4cf9dd --- /dev/null +++ b/cpukit/dev/can/can-bus.c @@ -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 + * 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 + * Copyright (C) 2002-2024 Pavel Pisa + * + * 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 +#include +#include +#include + +#include +#include +#include + +#include +#include + +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; +} diff --git a/cpukit/dev/can/can-devcommon.c b/cpukit/dev/can/can-devcommon.c new file mode 100644 index 0000000000..23489079f9 --- /dev/null +++ b/cpukit/dev/can/can-devcommon.c @@ -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 + * 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 + * Copyright (C) 2002-2024 Pavel Pisa + * + * 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 +#include + +#include +#include + +#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; +} diff --git a/cpukit/dev/can/can-quekern.c b/cpukit/dev/can/can-quekern.c new file mode 100644 index 0000000000..f8ecd4de4c --- /dev/null +++ b/cpukit/dev/can/can-quekern.c @@ -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 + * 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 + * Copyright (C) 2002-2024 Pavel Pisa + * + * 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 +#include + +#include +#include + +#include +#include +#include +#include + +#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; +} diff --git a/cpukit/dev/can/can-queue.c b/cpukit/dev/can/can-queue.c new file mode 100644 index 0000000000..7adfc5ea81 --- /dev/null +++ b/cpukit/dev/can/can-queue.c @@ -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 + * 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 + * Copyright (C) 2002-2024 Pavel Pisa + * + * 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 +#include + +#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; +} diff --git a/cpukit/include/dev/can/can-bittiming.h b/cpukit/include/dev/can/can-bittiming.h new file mode 100644 index 0000000000..57f5c1cac7 --- /dev/null +++ b/cpukit/include/dev/can/can-bittiming.h @@ -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 + * 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 + * Copyright (C) 2002-2024 Pavel Pisa + * 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 */ diff --git a/cpukit/include/dev/can/can-bus.h b/cpukit/include/dev/can/can-bus.h new file mode 100644 index 0000000000..7cd9d33439 --- /dev/null +++ b/cpukit/include/dev/can/can-bus.h @@ -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 + * 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 + * Copyright (C) 2002-2024 Pavel Pisa + * + * 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 +#include +#include +#include +#include + +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 */ diff --git a/cpukit/include/dev/can/can-devcommon.h b/cpukit/include/dev/can/can-devcommon.h new file mode 100644 index 0000000000..399b2f39c4 --- /dev/null +++ b/cpukit/include/dev/can/can-devcommon.h @@ -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 + * 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 + * Copyright (C) 2002-2024 Pavel Pisa + * + * 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 +#include +#include +#include +#include + +#include +#include +#include + +/** + * @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 */ diff --git a/cpukit/include/dev/can/can-filter.h b/cpukit/include/dev/can/can-filter.h new file mode 100644 index 0000000000..42db8eeca6 --- /dev/null +++ b/cpukit/include/dev/can/can-filter.h @@ -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 + * 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 + * Copyright (C) 2002-2024 Pavel Pisa + * + * 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 */ diff --git a/cpukit/include/dev/can/can-frame.h b/cpukit/include/dev/can/can-frame.h new file mode 100644 index 0000000000..d61148ab96 --- /dev/null +++ b/cpukit/include/dev/can/can-frame.h @@ -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 + * 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 + * Copyright (C) 2002-2024 Pavel Pisa + * + * 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 */ diff --git a/cpukit/include/dev/can/can-helpers.h b/cpukit/include/dev/can/can-helpers.h new file mode 100644 index 0000000000..b29911eaf6 --- /dev/null +++ b/cpukit/include/dev/can/can-helpers.h @@ -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 + * 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 + * Copyright (C) 2002-2024 Pavel Pisa + * + * 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 +#include + +/** + * @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 */ diff --git a/cpukit/include/dev/can/can-impl.h b/cpukit/include/dev/can/can-impl.h new file mode 100644 index 0000000000..c1927d0664 --- /dev/null +++ b/cpukit/include/dev/can/can-impl.h @@ -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 + * 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 + * Copyright (C) 2002-2024 Pavel Pisa + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/** + * @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 */ \ No newline at end of file diff --git a/cpukit/include/dev/can/can-queue.h b/cpukit/include/dev/can/can-queue.h new file mode 100644 index 0000000000..85b8e6d0b1 --- /dev/null +++ b/cpukit/include/dev/can/can-queue.h @@ -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 + * 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 + * Copyright (C) 2002-2024 Pavel Pisa + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +/** + * @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 */ diff --git a/cpukit/include/dev/can/can-stats.h b/cpukit/include/dev/can/can-stats.h new file mode 100644 index 0000000000..1ab98f218c --- /dev/null +++ b/cpukit/include/dev/can/can-stats.h @@ -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 + * 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 + * Copyright (C) 2002-2024 Pavel Pisa + * + * 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 */ diff --git a/cpukit/include/dev/can/can.h b/cpukit/include/dev/can/can.h new file mode 100644 index 0000000000..5b049330cb --- /dev/null +++ b/cpukit/include/dev/can/can.h @@ -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 + * 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 + * Copyright (C) 2002-2024 Pavel Pisa + * + * 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 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 diff --git a/spec/build/cpukit/cpuopts.yml b/spec/build/cpukit/cpuopts.yml index 1d28ace552..f1f6ac31c1 100644 --- a/spec/build/cpukit/cpuopts.yml +++ b/spec/build/cpukit/cpuopts.yml @@ -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 diff --git a/spec/build/cpukit/librtemscpu.yml b/spec/build/cpukit/librtemscpu.yml index 75a1a04594..722576dd26 100644 --- a/spec/build/cpukit/librtemscpu.yml +++ b/spec/build/cpukit/librtemscpu.yml @@ -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 diff --git a/spec/build/cpukit/optcanfifosize.yml b/spec/build/cpukit/optcanfifosize.yml new file mode 100644 index 0000000000..1748d142f3 --- /dev/null +++ b/spec/build/cpukit/optcanfifosize.yml @@ -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 +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 diff --git a/spec/build/cpukit/optcanqueueprios.yml b/spec/build/cpukit/optcanqueueprios.yml new file mode 100644 index 0000000000..b5d1eb4406 --- /dev/null +++ b/spec/build/cpukit/optcanqueueprios.yml @@ -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 +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