can: add support for a simple virtual CAN controller

This commit adds a support for a simple virtual CAN controller. This
controller can be used to test CAN stack functions and serves as an
inspiration for further controller implementations.

Signed-off-by: Michal Lenc <michallenc@seznam.cz>
Co-authored-by: Pavel Pisa <pisa@cmp.felk.cvut.cz>
This commit is contained in:
Michal Lenc
2024-05-26 13:25:14 +02:00
parent c3f4e215b1
commit 0cbb9eb9f9
3 changed files with 331 additions and 0 deletions

View File

@@ -0,0 +1,275 @@
/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */
/**
* @file
*
* @ingroup CANVirtual
*
* @brief This file implements virtual CAN chip.
*/
/*
* Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
* Implementation is based on original LinCAN - Linux CAN bus driver
* Part of OrtCAN project https://ortcan.sourceforge.net/
* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://control.fel.cvut.cz>
* Copyright (C) 2002-2024 Pavel Pisa <pisa@cmp.felk.cvut.cz>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**************************************************************************/
#include <stdlib.h>
#include <rtems.h>
#include <rtems/malloc.h>
#include <rtems/score/basedefs.h>
#include <dev/can/can-virtual.h>
#include <dev/can/can-helpers.h>
#include <dev/can/can-devcommon.h>
#include <dev/can/can.h>
#define CAN_VIRTUAL_PRIORITY 120
static rtems_task virtual_worker( rtems_task_argument arg )
{
struct rtems_can_chip *chip = (struct rtems_can_chip *)arg;
struct rtems_can_queue_ends_dev *qends_dev = chip->qends_dev;
struct rtems_can_queue_ends *qends = &qends_dev->base;
struct rtems_can_queue_edge *qedge;
struct rtems_can_queue_slot *slot;
int cmd;
while ( 1 ) {
while ( ( cmd = rtems_can_queue_test_outslot( qends, &qedge, &slot ) ) >= 0 ) {
if ( rtems_can_test_bit( RTEMS_CAN_CHIP_RUNNING, &chip->flags ) == 0 ) {
if ( cmd >= 0 ) {
rtems_can_queue_filter_frame_to_edges(
qends,
qedge,
&slot->frame,
CAN_FRAME_TXERR
);
rtems_can_stats_add_tx_error( &chip->chip_stats );
} else {
rtems_binary_semaphore_post( &chip->stop_sem );
}
} else if ( cmd >= 0 ) {
/* fill CAN message timestamp */
slot->frame.header.timestamp = rtems_can_fill_timestamp();
rtems_can_queue_filter_frame_to_edges(
qends,
qedge,
&slot->frame,
CAN_FRAME_LOCAL
);
rtems_can_stats_add_tx_done( &chip->chip_stats );
rtems_can_stats_add_rx_done( &chip->chip_stats );
}
rtems_can_queue_free_outslot( qends, qedge, slot );
}
rtems_binary_semaphore_wait( &qends_dev->worker_sem );
}
}
static int virtual_get_chip_info( struct rtems_can_chip *chip, int what )
{
int ret;
switch( what ) {
case RTEMS_CAN_CHIP_BITRATE:
case RTEMS_CAN_CHIP_DBITRATE:
ret = -EINVAL;
break;
case RTEMS_CAN_CHIP_NUSERS:
ret = atomic_load( &chip->used );
break;
case RTEMS_CAN_CHIP_FLAGS:
ret = atomic_load( &chip->flags );
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
/**
* @brief This function implements chip specific ioctl calls.
*
* @param chip Pointer to chip structure.
* @param command IOCTL command
* @param arg Void pointer to IOCL argument
*
* @return Negative value on error.
*/
static int virtual_chip_ioctl(
struct rtems_can_chip *chip,
ioctl_command_t command,
void *arg
)
{
int ret;
switch( command ) {
default:
ret = -EINVAL;
break;
}
return ret;
}
static int virtual_stop_chip ( struct rtems_can_chip *chip, struct timespec *ts )
{
rtems_interval timeout;
struct timespec curr;
struct timespec final;
struct timespec towait;
int ret;
rtems_mutex_lock( &chip->lock );
if ( rtems_can_test_bit( RTEMS_CAN_CHIP_RUNNING, &chip->flags ) == 0 ) {
/* Already stopped, return. */
rtems_mutex_unlock( &chip->lock );
return 0;
}
rtems_can_clear_bit( RTEMS_CAN_CHIP_RUNNING, &chip->flags );
rtems_binary_semaphore_post( &chip->qends_dev->worker_sem );
if ( ts != NULL ) {
/* Get absolute monotonic final time */
clock_gettime( CLOCK_MONOTONIC, &final );
rtems_timespec_add_to( &final, ts );
clock_gettime( CLOCK_MONOTONIC, &curr );
rtems_timespec_subtract( &curr, &final, &towait );
if ( towait.tv_sec < 0 ) {
ret = rtems_binary_semaphore_try_wait( &chip->stop_sem );
} else {
timeout = rtems_timespec_to_ticks( &towait );
ret = rtems_binary_semaphore_wait_timed_ticks(
&chip->stop_sem,
timeout
);
}
} else {
ret = rtems_binary_semaphore_try_wait( &chip->stop_sem );
}
if (ret < 0) {
rtems_can_queue_ends_flush_outlist( &chip->qends_dev->base );
}
rtems_binary_semaphore_post( &chip->qends_dev->worker_sem );
rtems_can_stats_set_state( &chip->chip_stats, CAN_STATE_STOPPED );
rtems_can_bus_notify_chip_stop( chip );
rtems_mutex_unlock( &chip->lock );
return 0;
}
static int virtual_start_chip ( struct rtems_can_chip *chip )
{
rtems_mutex_lock( &chip->lock );
if ( rtems_can_test_bit( RTEMS_CAN_CHIP_RUNNING, &chip->flags ) ) {
/* Chip was already started, skip */
rtems_mutex_unlock( &chip->lock );
return 0;
}
rtems_can_set_bit( RTEMS_CAN_CHIP_RUNNING, &chip->flags );
rtems_can_stats_reset( &chip->chip_stats );
rtems_can_stats_set_state( &chip->chip_stats, CAN_STATE_ERROR_ACTIVE );
rtems_mutex_unlock( &chip->lock );
return 0;
}
static void virtual_register( struct rtems_can_chip_ops *chip_ops )
{
chip_ops->start_chip = virtual_start_chip;
chip_ops->stop_chip = virtual_stop_chip;
chip_ops->chip_ioctl = virtual_chip_ioctl;
chip_ops->get_chip_timestamp = NULL;
chip_ops->get_chip_info = virtual_get_chip_info;
}
struct rtems_can_chip *rtems_can_virtual_initialize( void )
{
rtems_id worker_task_id;
rtems_status_code sc;
struct rtems_can_chip *chip;
chip = calloc( 1, sizeof( struct rtems_can_chip ) );
if ( !chip ) {
return NULL;
}
chip->type = CAN_VIRTUAL_TYPE;
chip->capabilities = RTEMS_CAN_CHIP_CAPABILITIES_FD;
chip->irq = 0;
chip->qends_dev = calloc( 1, sizeof( struct rtems_can_queue_ends_dev ) );
if ( !chip->qends_dev ) {
free( chip );
return NULL;
}
rtems_mutex_init( &chip->lock, "virtual_lock" );
rtems_binary_semaphore_init(
&chip->stop_sem,
"can_virtual_stop_sem"
);
rtems_can_queue_ends_init_chip(
chip,
"can_virtual_worker"
);
sc = rtems_task_create(
rtems_build_name( 'C', 'A', 'N', 'V' ),
CAN_VIRTUAL_PRIORITY,
RTEMS_MINIMUM_STACK_SIZE+0x1000,
RTEMS_DEFAULT_MODES,
RTEMS_DEFAULT_ATTRIBUTES,
&worker_task_id
);
if ( sc != RTEMS_SUCCESSFUL ) {
free( chip->qends_dev );
free( chip );
return NULL;
}
rtems_task_start( worker_task_id, virtual_worker,
(rtems_task_argument)chip );
virtual_register( &chip->chip_ops );
rtems_can_set_bit( RTEMS_CAN_CHIP_CONFIGURED, &chip->flags );
rtems_can_stats_set_state(&chip->chip_stats, CAN_STATE_STOPPED );
return chip;
}

View File

@@ -0,0 +1,54 @@
/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */
/**
* @file
*
* @ingroup CANVirtual
*
* @brief This file provides definitions for virtual CAN chip.
*/
/*
* Copyright (C) 2023-2024 Michal Lenc <michallenc@seznam.cz>
* Implementation is based on original LinCAN - Linux CAN bus driver
* Part of OrtCAN project https://ortcan.sourceforge.net/
* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://control.fel.cvut.cz>
* Copyright (C) 2002-2024 Pavel Pisa <pisa@cmp.felk.cvut.cz>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __DEV_CAN_CAN_VIRTUAL_H
#define __DEV_CAN_CAN_VIRTUAL_H
#define CAN_VIRTUAL_TYPE "virtual"
struct rtems_can_chip;
/**
* @brief This function initializes the virtual CAN controller
*
* @return Pointer to CAN chip structure on success, NULL otherwise.
*/
struct rtems_can_chip *rtems_can_virtual_initialize( void );
#endif /* __DEV_CAN_CAN_VIRTUAL_H */

View File

@@ -46,6 +46,7 @@ install:
- 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-virtual.h
- cpukit/include/dev/can/can.h
- destination: ${BSP_INCLUDEDIR}/dev/i2c
source:
@@ -548,6 +549,7 @@ source:
- cpukit/dev/can/can-devcommon.c
- cpukit/dev/can/can-quekern.c
- cpukit/dev/can/can-queue.c
- cpukit/dev/can/can-virtual.c
- cpukit/dev/flash/flashdev.c
- cpukit/dev/i2c/eeprom.c
- cpukit/dev/i2c/fpga-i2c-slave.c