nuttx/net/can/can.h

474 lines
16 KiB
C
Raw Normal View History

2020-06-15 10:23:25 +02:00
/****************************************************************************
* net/can/can.h
*
* SPDX-License-Identifier: Apache-2.0
*
2020-06-15 10:23:25 +02:00
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __NET_CAN_CAN_H
#define __NET_CAN_CAN_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <poll.h>
#include <nuttx/semaphore.h>
#include <nuttx/can.h>
#include <nuttx/net/net.h>
2020-06-15 10:23:25 +02:00
#include <nuttx/net/netdev.h>
#include "devif/devif.h"
#include "socket/socket.h"
#ifdef CONFIG_NET_CAN_NOTIFIER
# include <nuttx/wqueue.h>
#endif
#ifdef CONFIG_NET_CAN
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Allocate a new packet socket data callback */
#define can_callback_alloc(dev,conn) \
devif_callback_alloc(dev, &conn->sconn.list, &conn->sconn.list_tail)
2020-06-15 10:23:25 +02:00
#define can_callback_free(dev,conn,cb) \
devif_conn_callback_free(dev, cb, &conn->sconn.list, &conn->sconn.list_tail)
2020-06-15 10:23:25 +02:00
#ifndef CONFIG_NET_CAN_NBUFFERS
# define CONFIG_NET_CAN_NBUFFERS 0
#endif
2020-06-15 10:23:25 +02:00
/****************************************************************************
* Public Type Definitions
****************************************************************************/
/* This is a container that holds the poll-related information */
struct can_poll_s
{
FAR struct socket *psock; /* Needed to handle loss of connection */
FAR struct net_driver_s *dev; /* Needed to free the callback structure */
FAR struct pollfd *fds; /* Needed to handle poll events */
2020-06-15 10:23:25 +02:00
FAR struct devif_callback_s *cb; /* Needed to teardown the poll */
};
/* This "connection" structure describes the underlying state of the socket */
struct can_conn_s
{
/* Common prologue of all connection structures. */
struct socket_conn_s sconn;
2020-06-15 10:23:25 +02:00
FAR struct net_driver_s *dev; /* Reference to CAN device */
/* Read-ahead buffering.
*
* readahead - A singly linked list of type struct iob_qentry_s
* where the CAN/IP read-ahead data is retained.
*/
struct iob_queue_s readahead; /* remove Read-ahead buffering */
#if CONFIG_NET_RECV_BUFSIZE > 0
int32_t rcvbufs; /* Maximum amount of bytes queued in receive */
#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
2020-06-15 10:23:25 +02:00
/* CAN-specific content follows */
int16_t crefs; /* Reference count */
/* The following is a list of poll structures of threads waiting for
* socket events.
*/
net/can: fix poll setup to properly find and use free pollfd slots Bug Description: The original code always used conn->pollinfo[0] (the first element) to store new poll setup context, regardless of whether that slot was already in use. This caused multiple poll operations on the same CAN socket to overwrite each other's context, leading to: - Lost poll waiters when multiple threads poll the same socket - Memory corruption in pollfd structures - Undefined behavior when poll_teardown tries to clean up Root Cause: The code directly assigned `info = conn->pollinfo` without checking if the slot was available, effectively always using pollinfo[0]. When a second thread called poll() on the same socket, it would overwrite the first thread's poll context. Solution: 1. Initialize info to NULL instead of conn->pollinfo 2. Before setting up poll, iterate through all CONFIG_NET_CAN_NPOLLWAITERS slots to find the first free slot (where fds == NULL) 3. Return -EBUSY if no free slots are available 4. During teardown, properly mark the slot as free by setting fds = NULL Additional Changes: - Added CONFIG_NET_CAN_NPOLLWAITERS Kconfig option (default 4) to make the maximum number of concurrent poll waiters configurable - Changed hardcoded array size from 4 to CONFIG_NET_CAN_NPOLLWAITERS - Fixed lock ordering in teardown to ensure fds is cleared before unlock Impact: - Enables multiple threads to safely poll the same CAN socket concurrently - Prevents poll context corruption in multi-threaded applications - Provides proper resource management with -EBUSY when all slots are full - Makes the number of supported concurrent pollers configurable per use case Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
2025-12-01 19:30:04 +08:00
struct can_poll_s pollinfo[CONFIG_NET_CAN_NPOLLWAITERS];
2020-06-15 10:23:25 +02:00
#ifdef CONFIG_NET_CANPROTO_OPTIONS
struct can_filter filters[CONFIG_NET_CAN_RAW_FILTER_MAX];
int32_t filter_count;
# ifdef CONFIG_NET_CAN_ERRORS
can_err_mask_t err_mask;
# endif
#endif
2020-06-15 10:23:25 +02:00
};
/****************************************************************************
* Public Data
****************************************************************************/
#ifdef __cplusplus
# define EXTERN extern "C"
extern "C"
{
#else
# define EXTERN extern
#endif
EXTERN const struct sock_intf_s g_can_sockif;
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: can_alloc()
*
* Description:
* Allocate a new, uninitialized NetLink connection structure. This is
* normally something done by the implementation of the socket() API
*
****************************************************************************/
FAR struct can_conn_s *can_alloc(void);
/****************************************************************************
* Name: can_free()
*
* Description:
* Free a NetLink connection structure that is no longer in use. This
* should be done by the implementation of close().
*
****************************************************************************/
void can_free(FAR struct can_conn_s *conn);
/****************************************************************************
* Name: can_nextconn()
*
* Description:
* Traverse the list of allocated NetLink connections
*
* Assumptions:
* This function is called from NetLink device logic.
*
****************************************************************************/
FAR struct can_conn_s *can_nextconn(FAR struct can_conn_s *conn);
/****************************************************************************
* Name: can_conn_list_lock
* can_conn_list_unlock
*
* Description:
* Lock and unlock the CAN connection list. This is used to protect
* the list of active connections.
*
* Assumptions:
* This function is called from driver.
*
****************************************************************************/
void can_conn_list_lock(void);
void can_conn_list_unlock(void);
/****************************************************************************
* Name: can_active()
*
* Description:
* Traverse the list of NetLink connections that match dev
*
* Input Parameters:
* dev - The device to search for.
* conn - The current connection; may be NULL to start the search at the
* beginning
*
* Assumptions:
* This function is called from NetLink device logic.
*
****************************************************************************/
FAR struct can_conn_s *can_active(FAR struct net_driver_s *dev,
FAR struct can_conn_s *conn);
2020-06-15 10:23:25 +02:00
/****************************************************************************
* Name: can_callback
*
* Description:
* Inform the application holding the packet socket of a change in state.
*
* Returned Value:
* OK if packet has been processed, otherwise ERROR.
*
* Assumptions:
* This function is called from network logic at with the network locked.
*
****************************************************************************/
uint32_t can_callback(FAR struct net_driver_s *dev,
FAR struct can_conn_s *conn, uint32_t flags);
2020-06-15 10:23:25 +02:00
/****************************************************************************
* Name: can_datahandler
*
* Description:
* Handle data that is not accepted by the application. This may be called
* either (1) from the data receive logic if it cannot buffer the data, or
* (2) from the CAN event logic is there is no listener in place ready to
* receive the data.
*
* Input Parameters:
net/l2/l3/l4: add support of iob offload 1. Add new config CONFIG_NET_LL_GUARDSIZE to isolation of l2 stack, which will benefit l3(IP) layer for multi-MAC(l2) implementation, especially in some NICs such as celluler net driver. new configuration options: CONFIG_NET_LL_GUARDSIZE CONFIG_NET_LL_GUARDSIZE will reserved l2 buffer header size of network buffer to isolate the L2/L3 (MAC/IP) data on network layer, which will be beneficial to L3 network layer protocol transparent transmission and forwarding ------------------------------------------------------------ Layout of frist iob entry: iob_data (aligned by CONFIG_IOB_ALIGNMENT) | | io_offset(CONFIG_NET_LL_GUARDSIZE) | | ------------------------------------------------- iob | Reserved | io_len | ------------------------------------------------- ------------------------------------------------------------- Layout of different NICs implementation: iob_data (aligned by CONFIG_IOB_ALIGNMENT) | | io_offset(CONFIG_NET_LL_GUARDSIZE) | | ------------------------------------------------- Ethernet | Reserved | ETH_HDRLEN | io_len | ---------------------------------|--------------- 8021Q | Reserved | ETH_8021Q_HDRLEN | io_len | ---------------------------------|--------------- ipforward | Reserved | io_len | ------------------------------------------------- -------------------------------------------------------------------- 2. Support iob offload to l2 driver to avoid unnecessary memory copy Support send/receive iob vectors directly between the NICs and l3/l4 stack to avoid unnecessary memory copies, especially on hardware that supports Scatter/gather, which can greatly improve performance. new interface to support iob offload: ------------------------------------------ | IOB version | original | |----------------------------------------| | devif_iob_poll() | devif_poll() | | ... | ... | ------------------------------------------ -------------------------------------------------------------------- 1> NIC hardware support Scatter/gather transfer TX: tcp_poll()/udp_poll()/pkt_poll()/...(l3|l4) / \ / \ devif_poll_[l3|l4]_connections() devif_iob_send() (nocopy:udp/icmp/...) / \ (copy:tcp) / \ devif_iob_poll("NIC"_txpoll) callback() // "NIC"_txpoll | dev->d_iob: | --------------- --------------- io_data iob1 | | | iob3 | | | \ --------------- --------------- --------------- | --------------- | iob0 | | | | iob2 | | | | --------------- | --------------- | \ | / / \ | / / ---------------------------------------------- NICs io vector | | | | | | | | | | ---------------------------------------------- RX: [tcp|udp|icmp|...]ipv[4|6]_data_handler()(iob_concat/append to readahead) | | [tcp|udp|icmp|...]_ipv[4|6]_in()/... | | pkt/ipv[4/6]_input()/... | | NICs io vector receive(iov_base to each iobs) -------------------------------------------------------------------- 2> CONFIG_IOB_BUFSIZE is greater than MTU: TX: "(CONFIG_IOB_BUFSIZE) > (MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE + CONFIG_NET_LL_GUARDSIZE)" tcp_poll()/udp_poll()/pkt_poll()/...(l3|l4) / \ / \ devif_poll_[l3|l4]_connections() devif_iob_send() (nocopy:udp/icmp/...) / \ (copy:tcp) / \ devif_iob_poll("NIC"_txpoll) callback() // "NIC"_txpoll | "NIC"_send() (dev->d_iob->io_data[CONFIG_NET_LL_GUARDSIZE - NET_LL_HDRLEN(dev)]) RX: [tcp|udp|icmp|...]ipv[4|6]_data_handler()(iob_concat/append to readahead) | | [tcp|udp|icmp|...]_ipv[4|6]_in()/... | | pkt/ipv[4/6]_input()/... | | NICs io vector receive(iov_base to io_data) -------------------------------------------------------------------- 3> Compatible with all old flat buffer NICs TX: tcp_poll()/udp_poll()/pkt_poll()/...(l3|l4) / \ / \ devif_poll_[l3|l4]_connections() devif_iob_send() (nocopy:udp/icmp/...) / \ (copy:tcp) / \ devif_iob_poll(devif_poll_callback()) devif_poll_callback() /* new interface, gather iobs to flat buffer */ / \ / \ devif_poll("NIC"_txpoll) "NIC"_send()(dev->d_buf) RX: [tcp|udp|icmp|...]ipv[4|6]_data_handler()(iob_concat/append to readahead) | | [tcp|udp|icmp|...]_ipv[4|6]_in()/... | | netdev_input() /* new interface, Scatter/gather flat/iob buffer */ | | pkt/ipv[4|6]_input()/... | | NICs io vector receive(Orignal flat buffer) 3. Iperf passthrough on NuttX simulator: ------------------------------------------------- | Protocol | Server | Client | | |-----------------------------------------------| | TCP | 813 | 834 | Mbits/sec | | TCP(Offload) | 1720 | 1100 | Mbits/sec | | UDP | 22 | 757 | Mbits/sec | | UDP(Offload) | 25 | 1250 | Mbits/sec | ------------------------------------------------- Signed-off-by: chao an <anchao@xiaomi.com>
2022-11-23 19:39:40 +08:00
* dev - The device which as active when the event was detected.
2020-06-15 10:23:25 +02:00
* conn - A pointer to the CAN connection structure
*
* Returned Value:
* The number of bytes actually buffered is returned. This will be
* negative or zero or equal to buflen; partial packets are not buffered.
2020-06-15 10:23:25 +02:00
*
* Assumptions:
* - The caller has checked that CAN_NEWDATA is set in flags and that is no
* other handler available to process the incoming data.
* - Called from network stack logic with the network stack locked
*
****************************************************************************/
int can_datahandler(FAR struct net_driver_s *dev,
FAR struct can_conn_s *conn);
2020-06-15 10:23:25 +02:00
/****************************************************************************
* Name: can_recvmsg
*
* Description:
* recvmsg() receives messages from a socket, and may be used to receive
* data on a socket whether or not it is connection-oriented.
*
* If from is not NULL, and the underlying protocol provides the source
* address, this source address is filled in. The argument 'msg_namelen'
2020-06-15 10:23:25 +02:00
* initialized to the size of the buffer associated with from, and modified
* on return to indicate the actual size of the address stored there.
*
* Input Parameters:
* psock A pointer to a NuttX-specific, internal socket structure
* msg Buffer to receive the message
2020-06-15 10:23:25 +02:00
* flags Receive flags (ignored)
*
****************************************************************************/
2020-06-15 10:23:25 +02:00
ssize_t can_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg,
int flags);
/****************************************************************************
* Name: can_poll
*
* Description:
* Poll a CAN connection structure for availability of TX data
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
* conn - The CAN "connection" to poll for TX data
*
* Returned Value:
* None
*
* Assumptions:
* Called from network stack logic with the network stack locked
*
****************************************************************************/
void can_poll(FAR struct net_driver_s *dev, FAR struct can_conn_s *conn);
/****************************************************************************
* Name: psock_can_cansend
*
* Description:
* psock_can_cansend() returns a value indicating if a write to the socket
* would block. It is still possible that the write may block if another
* write occurs first.
*
* Input Parameters:
* psock An instance of the internal socket structure.
*
* Returned Value:
* OK
* At least one byte of data could be successfully written.
* -EWOULDBLOCK
* There is no room in the output buffer.
* -EBADF
* An invalid descriptor was specified.
*
* Assumptions:
* None
*
****************************************************************************/
int psock_can_cansend(FAR struct socket *psock);
2020-06-15 10:23:25 +02:00
/****************************************************************************
* Name: can_sendmsg
2020-06-15 10:23:25 +02:00
*
* Description:
* The can_sendmsg() call may be used only when the packet socket is
2020-06-15 10:23:25 +02:00
* in a connected state (so that the intended recipient is known).
*
* Input Parameters:
* psock An instance of the internal socket structure.
* msg CAN frame and optional CMSG
* flags Send flags (ignored)
2020-06-15 10:23:25 +02:00
*
* Returned Value:
* On success, returns the number of characters sent. On error,
* a negated errno value is retruend. See sendmsg() for the complete list
2020-06-15 10:23:25 +02:00
* of return values.
*
****************************************************************************/
ssize_t can_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
int flags);
2020-06-15 10:23:25 +02:00
/****************************************************************************
* Name: can_readahead_signal
*
* Description:
* Read-ahead data has been buffered. Signal all threads waiting for
* read-ahead data to become available.
*
* When read-ahead data becomes available, *all* of the workers waiting
* for read-ahead data will be executed. If there are multiple workers
* waiting for read-ahead data then only the first to execute will get the
* data. Others will need to call can_readahead_notifier_setup() once
* again.
*
* Input Parameters:
* conn - The CAN connection where read-ahead data was just buffered.
*
* Returned Value:
* None.
*
****************************************************************************/
#ifdef CONFIG_NET_CAN_NOTIFIER
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
*
* Description:
* can_setsockopt() sets the CAN-protocol option specified by the
* 'option' argument to the value pointed to by the 'value' argument for
* the socket specified by the 'psock' argument.
*
* See <nuttx/can.h> for the a complete list of values of CAN protocol
* options.
*
* Input Parameters:
* psock Socket structure of socket to operate on
* level Protocol level to set the option
* option identifies the option to set
* value Points to the argument value
* value_len The length of the argument value
*
* Returned Value:
* Returns zero (OK) on success. On failure, it returns a negated errno
* value to indicate the nature of the error. See psock_setcockopt() for
* the list of possible error values.
*
****************************************************************************/
#ifdef CONFIG_NET_CANPROTO_OPTIONS
int can_setsockopt(FAR struct socket *psock, int level, int option,
FAR const void *value, socklen_t value_len);
#endif
/****************************************************************************
* Name: can_getsockopt
*
* Description:
* can_getsockopt() retrieves the value for the option specified by the
* 'option' argument for the socket specified by the 'psock' argument. If
* the size of the option value is greater than 'value_len', the value
* stored in the object pointed to by the 'value' argument will be silently
* truncated. Otherwise, the length pointed to by the 'value_len' argument
* will be modified to indicate the actual length of the 'value'.
*
* See <sys/socket.h> a complete list of values for the socket-level
* 'option' argument. Protocol-specific options are are protocol specific
* header files (such as nuttx/can.h for the case of the CAN protocol).
*
* Input Parameters:
* psock Socket structure of the socket to query
* level Protocol level to set the option
* option identifies the option to get
* value Points to the argument value
* value_len The length of the argument value
*
* Returned Value:
* Returns zero (OK) on success. On failure, it returns a negated errno
* value to indicate the nature of the error. See psock_getsockopt() for
* the complete list of appropriate return error codes.
*
****************************************************************************/
#ifdef CONFIG_NET_CANPROTO_OPTIONS
int can_getsockopt(FAR struct socket *psock, int level, int option,
FAR void *value, FAR socklen_t *value_len);
#endif
2020-06-15 10:23:25 +02:00
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* CONFIG_NET_CAN */
#endif /* __NET_CAN_CAN_H */