Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Documentation/components/drivers/character/can.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
CAN Drivers
===========

NuttX supports only a very low-level CAN driver. This driver
supports only the data exchange and does not include any
NuttX supports character device CAN a very low-level CAN driver and
SocketCAN as a more high level (and more overhead) option.
This driver supports only the data exchange and does not include any
high-level CAN protocol. The NuttX CAN driver is split into two
parts:

Expand Down
101 changes: 94 additions & 7 deletions Documentation/components/net/socketcan.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
========================
=========
SocketCAN
=========

SocketCAN Device Drivers
========================

Expand All @@ -7,19 +10,19 @@ SocketCAN Device Drivers
The structure struct net_driver_s defines the interface and is
passed to the network via ``netdev_register()``.

- ``include/nuttx/can.h``. CAN & CAN FD frame data structures.
- ``include/nuttx/can.h``. CAN & CAN FD frame data structures and dlc to len size

.. code-block:: c

uint8_t can_bytes2dlc(uint8_t nbytes);
uint8_t can_dlc2bytes(uint8_t dlc);

- ``int netdev_register(FAR struct net_driver_s *dev, enum net_lltype_e lltype)'``.
Each driver registers itself by calling ``netdev_register()``.

- ``Include/nuttx/net/can.h``. contains lookup tables for CAN
dlc to CAN FD len sizes named

.. code-block:: c

extern const uint8_t g_can_dlc_to_len[16];
extern const uint8_t g_len_to_can_dlc[65];

- **Initialization sequence is as follows**.

#. ``xxx_netinitialize(void)`` is called on startup of NuttX in this
Expand Down Expand Up @@ -59,3 +62,87 @@ SocketCAN Device Drivers
matches the size of a ``struct can_frame`` or
``struct canfd_frame`` then you cast the content of the
``net_driver_s.d_buf`` pointer to the correct CAN frame struct

SocketCAN protocol stack
========================

SocketCAN is a CAN protocol stack implementation based on the BSD socket API,
providing a more standardized and flexible CAN communication interface.
SocketCAN uses the network protocol stack framework, allowing applications
to use standard socket system calls (such as ``socket()``, ``bind()``,
``send()``, ``recv()``, etc.) for CAN communication.

Architecture
------------

The SocketCAN implementation follows the standard network layer hierarchy:

#. **Application Layer Interface**: Uses standard socket API
(AF_CAN address family)
#. **Protocol Layer**: CAN protocol processing (located in ``net/can/``)
#. **Device Layer**: CAN network device drivers

File Locations
--------------

Files supporting SocketCAN can be found in the following locations:

- **Protocol Implementation**: The SocketCAN protocol stack is located in
the ``net/can/`` directory
- **Header File**: ``include/nuttx/net/can.h``
- **Main Modules**:

- ``can_conn.c`` - Connection management
- ``can_sockif.c`` - Socket interface implementation
- ``can_sendmsg.c`` - Message transmission
- ``can_sendmsg_buffered.c`` - Message transmission with buffering
- ``can_recvmsg.c`` - Message reception
- ``can_poll.c`` - Polling support
- ``can_callback.c`` - Callback handling

Configuration Options
---------------------

To enable SocketCAN, configure the following options:

- ``CONFIG_NET_CAN`` - Enable SocketCAN support
- ``CONFIG_NET_CAN_NOTIFIER`` - Enable CAN notifier (optional)
- ``CONFIG_NET_CAN_NBUFFERS`` - Number of CAN buffers
- ``CONFIG_NET_RECV_BUFSIZE`` - Receive buffer size

Usage Notes
-----------

SocketCAN uses the standard socket programming model:

.. code-block:: c

/* Create CAN socket */
int sock = socket(AF_CAN, SOCK_RAW, CAN_RAW);

/* Bind to CAN interface */
struct sockaddr_can addr;
addr.can_family = AF_CAN;
addr.can_ifindex = if_nametoindex("can0");
bind(sock, (struct sockaddr *)&addr, sizeof(addr));

/* Send CAN frame */
struct can_frame frame;
frame.can_id = 0x123;
frame.can_dlc = 8;
/* Fill data */
send(sock, &frame, sizeof(frame), 0);

/* Receive CAN frame */
recv(sock, &frame, sizeof(frame), 0);

Features
--------

- **Standard Socket API**: Uses familiar socket programming interface
- **Filtering Support**: Set CAN ID filters via socket options
- **Non-blocking I/O**: Supports non-blocking mode and polling
- **Multiple Connections**: Supports multiple sockets accessing the same CAN bus simultaneously
- **Read Buffering**: Supports data read buffering to prevent data loss
- **CAN FD Support**: Supports CAN FD frames if enabled in configuration
- **Extensible**: Easy to extend for additional CAN protocols
2 changes: 1 addition & 1 deletion net/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ config NET_MAX_RECV_BUFSIZE

config NET_SEND_BUFSIZE
int "Net Default Send buffer size"
depends on NET_TCP_WRITE_BUFFERS || NET_UDP_WRITE_BUFFERS
depends on NET_TCP_WRITE_BUFFERS || NET_UDP_WRITE_BUFFERS || NET_CAN_WRITE_BUFFERS
default 0
---help---
This is the default value for send buffer size.
Expand Down
8 changes: 7 additions & 1 deletion net/can/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@ if(CONFIG_NET_CAN)
set(SRCS)

# Socket layer
list(APPEND SRCS can_sockif.c can_sendmsg.c can_recvmsg.c)
list(APPEND SRCS can_sockif.c can_recvmsg.c)

if(CONFIG_NET_CAN_WRITE_BUFFERS)
list(APPEND SRCS can_sendmsg_buffered.c)
else()
list(APPEND SRCS can_sendmsg.c)
endif()

if(CONFIG_NET_CAN_NOTIFIER)
list(APPEND SRCS can_notifier.c)
Expand Down
11 changes: 11 additions & 0 deletions net/can/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ config NET_CAN_HAVE_ERRORS
bool
default n

config NET_CAN_WRITE_BUFFERS
bool "Enable CAN write buffering"
default n
select NTE_WRITE_BUFFERS
---help---
Write buffers allows buffering of ongoing CAN packets, providing
for higher performance, nonblocking output.

You might want to disable CAN write buffering on a highly memory
memory constrained system where there are no performance issues.

config CAN_PREALLOC_CONNS
int "Preallocated CAN socket connections"
default 4
Expand Down
5 changes: 5 additions & 0 deletions net/can/Make.defs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ ifeq ($(CONFIG_NET_CAN),y)
# Socket layer

SOCK_CSRCS += can_sockif.c
ifeq ($(CONFIG_NET_CAN_WRITE_BUFFERS),y)
SOCK_CSRCS += can_sendmsg_buffered.c
else
SOCK_CSRCS += can_sendmsg.c
endif

SOCK_CSRCS += can_recvmsg.c

ifeq ($(CONFIG_NET_CAN_NOTIFIER),y)
Expand Down
37 changes: 37 additions & 0 deletions net/can/can.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,25 @@ struct can_conn_s
int32_t recv_buffnum; /* Recv buffer number */
#endif

#if CONFIG_NET_SEND_BUFSIZE > 0
int32_t sndbufs; /* Maximum amount of bytes queued in send */
sem_t sndsem; /* Semaphore signals send completion */
#endif

#ifdef CONFIG_NET_CAN_WRITE_BUFFERS
/* Write buffering
*
* write_q - The queue of unsent I/O buffers. The head of this
* list may be partially sent. FIFO ordering.
*/

struct iob_queue_s write_q; /* Write buffering for can messages */

/* Callback instance for can send */

FAR struct devif_callback_s *sndcb;
#endif

/* CAN-specific content follows */

int16_t crefs; /* Reference count */
Expand Down Expand Up @@ -356,6 +375,24 @@ ssize_t can_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
void can_readahead_signal(FAR struct can_conn_s *conn);
#endif

/****************************************************************************
* Name: can_sendbuffer_notify
*
* Description:
* Notify the send buffer semaphore
*
* Input Parameters:
* conn - The CAN connection of interest
*
* Assumptions:
* Called from user logic with the network locked.
*
****************************************************************************/

#if CONFIG_NET_SEND_BUFSIZE > 0
void can_sendbuffer_notify(FAR struct can_conn_s *conn);
#endif /* CONFIG_NET_SEND_BUFSIZE */

/****************************************************************************
* Name: can_setsockopt
*
Expand Down
24 changes: 23 additions & 1 deletion net/can/can_conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,27 @@ void can_free(FAR struct can_conn_s *conn)

dq_rem(&conn->sconn.node, &g_active_can_connections);

#ifdef CONFIG_NET_CAN_WRITE_BUFFERS
/* Free the write queue */

iob_free_queue(&conn->write_q);

# if CONFIG_NET_SEND_BUFSIZE > 0
/* Notify the send buffer available */

can_sendbuffer_notify(conn);
# endif /* CONFIG_NET_SEND_BUFSIZE */

#endif

#if CONFIG_NET_SEND_BUFSIZE > 0
nxsem_destroy(&conn->sndsem);
#endif

/* Free the readahead queue */

iob_free_queue(&conn->readahead);

/* Free the connection. */

NET_BUFPOOL_FREE(g_can_connections, conn);
Expand Down Expand Up @@ -203,7 +224,8 @@ FAR struct can_conn_s *can_active(FAR struct net_driver_s *dev,
{
while ((conn = can_nextconn(conn)) != NULL)
{
if (conn->dev == NULL || conn->dev == dev)
if ((conn->dev == NULL && _SS_ISBOUND(conn->sconn.s_flags)) ||
conn->dev == dev)
{
break;
}
Expand Down
15 changes: 15 additions & 0 deletions net/can/can_getsockopt.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,21 @@ int can_getsockopt(FAR struct socket *psock, int level, int option,
break;
#endif

#if CONFIG_NET_SEND_BUFSIZE > 0
case SO_SNDBUF:
/* Verify that option is the size of an 'int'. Should also check
* that 'value' is properly aligned for an 'int'
*/

if (*value_len < sizeof(int))
{
return -EINVAL;
}

*(FAR int *)value = conn->sndbufs;
break;
#endif

default:
nerr("ERROR: Unrecognized RAW CAN socket option: %d\n", option);
ret = -ENOPROTOOPT;
Expand Down
5 changes: 5 additions & 0 deletions net/can/can_recvmsg.c
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,11 @@ ssize_t can_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg,
/* Get the device driver that will service this transfer */

dev = conn->dev;
if (dev == NULL && _SS_ISBOUND(conn->sconn.s_flags))
{
dev = netdev_default();
}

if (dev == NULL)
{
ret = -ENODEV;
Expand Down
Loading
Loading