From commits-return-7953-archive-asf-public=cust-asf.ponee.io@nuttx.apache.org Thu Apr 23 13:31:08 2020 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [207.244.88.153]) by mx-eu-01.ponee.io (Postfix) with SMTP id 7A0A618069C for ; Thu, 23 Apr 2020 15:31:07 +0200 (CEST) Received: (qmail 89687 invoked by uid 500); 23 Apr 2020 13:31:06 -0000 Mailing-List: contact commits-help@nuttx.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@nuttx.apache.org Delivered-To: mailing list commits@nuttx.apache.org Received: (qmail 89539 invoked by uid 99); 23 Apr 2020 13:31:06 -0000 Received: from ec2-52-202-80-70.compute-1.amazonaws.com (HELO gitbox.apache.org) (52.202.80.70) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 23 Apr 2020 13:31:06 +0000 Received: by gitbox.apache.org (ASF Mail Server at gitbox.apache.org, from userid 33) id 2B389890AE; Thu, 23 Apr 2020 13:31:06 +0000 (UTC) Date: Thu, 23 Apr 2020 13:31:12 +0000 To: "commits@nuttx.apache.org" Subject: [incubator-nuttx] 07/31: Added basic poll()/select support MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit From: gnutt@apache.org In-Reply-To: <158764866532.498.11500477209365117867@gitbox.apache.org> References: <158764866532.498.11500477209365117867@gitbox.apache.org> X-Git-Host: gitbox.apache.org X-Git-Repo: incubator-nuttx X-Git-Refname: refs/heads/SocketCAN X-Git-Reftype: branch X-Git-Rev: fa8f6583717a06ddbbe77f65a1dce002398da6ab X-Git-NotificationType: diff X-Git-Multimail-Version: 1.5.dev Auto-Submitted: auto-generated Message-Id: <20200423133106.2B389890AE@gitbox.apache.org> This is an automated email from the ASF dual-hosted git repository. gnutt pushed a commit to branch SocketCAN in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git commit fa8f6583717a06ddbbe77f65a1dce002398da6ab Author: Peter van der Perk AuthorDate: Fri Feb 21 16:24:54 2020 +0100 Added basic poll()/select support Futhermore addded stubs for SocketCAN sockopts --- include/netpacket/can.h | 18 +++ include/nuttx/can.h | 41 +++++ include/nuttx/mm/iob.h | 3 + include/nuttx/wqueue.h | 3 +- include/sys/socket.h | 10 ++ net/can/Kconfig | 19 +++ net/can/Make.defs | 8 + net/can/can.h | 96 ++++++++++-- net/can/can_callback.c | 145 +++++++++++++++++- net/can/can_conn.c | 22 +-- net/can/can_getsockopt.c | 154 +++++++++++++++++++ net/can/{can_callback.c => can_notifier.c} | 50 +++---- net/can/can_poll.c | 2 +- net/can/can_recvfrom.c | 166 +++++++++++++++++++- net/can/{can_callback.c => can_setsockopt.c} | 102 ++++++++++--- net/can/can_sockif.c | 216 ++++++++++++++++++--------- net/socket/Kconfig | 6 + net/socket/getsockopt.c | 6 + 18 files changed, 909 insertions(+), 158 deletions(-) diff --git a/include/netpacket/can.h b/include/netpacket/can.h index b93bb21..45edab5 100644 --- a/include/netpacket/can.h +++ b/include/netpacket/can.h @@ -47,6 +47,9 @@ #define CAN_EFF_MASK 0x1fffffff /* Extended frame format (EFF) */ #define CAN_ERR_MASK 0x1fffffff /* Omit EFF, RTR, ERR flags */ +#define CAN_MTU (sizeof(struct can_frame)) +#define CANFD_MTU (sizeof(struct canfd_frame)) + /* PF_CAN protocols */ #define CAN_RAW 1 /* RAW sockets */ @@ -58,6 +61,21 @@ #define CAN_J1939 7 /* SAE J1939 */ #define CAN_NPROTO 8 +/* CAN_RAW socket options */ + +#define CAN_RAW_FILTER (__SO_PROTOCOL + 0) + /* set 0 .. n can_filter(s) */ +#define CAN_RAW_ERR_FILTER (__SO_PROTOCOL + 1) + /* set filter for error frames */ +#define CAN_RAW_LOOPBACK (__SO_PROTOCOL + 2) + /* local loopback (default:on) */ +#define CAN_RAW_RECV_OWN_MSGS (__SO_PROTOCOL + 3) + /* receive my own msgs (default:off) */ +#define CAN_RAW_FD_FRAMES (__SO_PROTOCOL + 4) + /* allow CAN FD frames (default:off) */ +#define CAN_RAW_JOIN_FILTERS (__SO_PROTOCOL + 5) + /* all filters must match to trigger */ + /**************************************************************************** * Public Types ****************************************************************************/ diff --git a/include/nuttx/can.h b/include/nuttx/can.h index fd86b74..02f80a8 100644 --- a/include/nuttx/can.h +++ b/include/nuttx/can.h @@ -45,6 +45,8 @@ # include #endif +#include + #ifdef CONFIG_NET_CAN /************************************************************************************ @@ -187,8 +189,26 @@ * Public Types ************************************************************************************/ +typedef FAR void *CAN_HANDLE; + +struct can_response_s +{ + sq_entry_t flink; + + /* Message-specific data may follow */ +}; //FIXME remvoe + + typedef uint32_t canid_t; +/* + * Controller Area Network Error Message Frame Mask structure + * + * bit 0-28 : error class mask (see include/uapi/linux/can/error.h) + * bit 29-31 : set to zero + */ +typedef uint32_t can_err_mask_t; + /* CAN payload length and DLC definitions according to ISO 11898-1 */ #define CAN_MAX_DLC 8 #define CAN_MAX_DLEN 8 @@ -256,6 +276,27 @@ struct canfd_frame { }; +/** + * struct can_filter - CAN ID based filter in can_register(). + * @can_id: relevant bits of CAN ID which are not masked out. + * @can_mask: CAN mask (see description) + * + * Description: + * A filter matches, when + * + * & mask == can_id & mask + * + * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can + * filter for error message frames (CAN_ERR_FLAG bit set in mask). + */ +struct can_filter { + canid_t can_id; + canid_t can_mask; +}; + +#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */ +#define CAN_RAW_FILTER_MAX 512 /* maximum number of can_filter set via setsockopt() */ + /************************************************************************************ * Public Function Prototypes ************************************************************************************/ diff --git a/include/nuttx/mm/iob.h b/include/nuttx/mm/iob.h index 8984af6..cabd2ff 100644 --- a/include/nuttx/mm/iob.h +++ b/include/nuttx/mm/iob.h @@ -220,6 +220,9 @@ enum iob_user_e #ifdef CONFIG_WIRELESS_BLUETOOTH IOBUSER_WIRELESS_BLUETOOTH, #endif +#if defined(CONFIG_NET_CAN) + IOBUSER_NET_CAN_READAHEAD, +#endif IOBUSER_GLOBAL, IOBUSER_NENTRIES /* MUST BE LAST ENTRY */ }; diff --git a/include/nuttx/wqueue.h b/include/nuttx/wqueue.h index f4e7260..6f88a3a 100644 --- a/include/nuttx/wqueue.h +++ b/include/nuttx/wqueue.h @@ -289,7 +289,8 @@ enum work_evtype_e WORK_TCP_DISCONNECT, /* Notify loss of TCP connection */ WORK_UDP_READAHEAD, /* Notify that UDP read-ahead data is available */ WORK_UDP_WRITEBUFFER, /* Notify that UDP write buffer is empty */ - WORK_NETLINK_RESPONSE /* Notify that Netlink response is available */ + WORK_NETLINK_RESPONSE, /* Notify thtat Netlink response is available */ + WORK_CAN_READAHEAD /* Notify that CAN read-ahead data is available */ }; /* This structure describes one notification and is provided as input to diff --git a/include/sys/socket.h b/include/sys/socket.h index c894acc..efba67f 100644 --- a/include/sys/socket.h +++ b/include/sys/socket.h @@ -202,6 +202,15 @@ * return: int */ + +/* The options are unsupported but included for compatibility + * and portability + */ +#define SO_TIMESTAMP 29 +#define SO_SNDBUFFORCE 32 +#define SO_RCVBUFFORCE 33 +#define SO_RXQ_OVFL 40 + /* Protocol-level socket operations. */ #define SOL_IP 1 /* See options in include/netinet/ip.h */ @@ -212,6 +221,7 @@ #define SOL_L2CAP 6 /* See options in include/netpacket/bluetooth.h */ #define SOL_SCO 7 /* See options in include/netpacket/bluetooth.h */ #define SOL_RFCOMM 8 /* See options in include/netpacket/bluetooth.h */ +#define SOL_CAN_RAW 9 /* See options in include/netpacket/can.h */ /* Protocol-level socket options may begin with this value */ diff --git a/net/can/Kconfig b/net/can/Kconfig index 2ae1f7b..2272db7 100644 --- a/net/can/Kconfig +++ b/net/can/Kconfig @@ -21,6 +21,25 @@ config CAN_CONNS default 4 ---help--- Maximum number of CAN connections (all tasks). + +config NET_CAN_SOCK_OPTS + bool "sockopt support" + default n + select NET_CANPROTO_OPTIONS + ---help--- + Enable support for the CAN socket options + +config NET_CAN_NOTIFIER + bool "Support CAN notifications" + default n + depends on SCHED_WORKQUEUE + select WQUEUE_NOTIFIER + ---help--- + Enable building of CAN notifier logic that will execute a worker + function on the low priority work queue when read-ahead data + is available or when a CAN connection is lost. This is is a general + purpose notifier, but was developed specifically to support poll() + logic where the poll must wait for these events. endif # NET_CAN endmenu # CAN Socket Support diff --git a/net/can/Make.defs b/net/can/Make.defs index d078a05..3204b3b 100644 --- a/net/can/Make.defs +++ b/net/can/Make.defs @@ -28,6 +28,14 @@ SOCK_CSRCS += can_sockif.c SOCK_CSRCS += can_send.c SOCK_CSRCS += can_recvfrom.c +ifeq ($(CONFIG_NET_CAN_NOTIFIER),y) +SOCK_CSRCS += can_notifier.c +endif + +ifeq ($(CONFIG_NET_CANPROTO_OPTIONS),y) +SOCK_CSRCS += can_setsockopt.c can_getsockopt.c +endif + NET_CSRCS += can_conn.c NET_CSRCS += can_input.c NET_CSRCS += can_callback.c diff --git a/net/can/can.h b/net/can/can.h index c2b6857..46c7425 100644 --- a/net/can/can.h +++ b/net/can/can.h @@ -32,11 +32,16 @@ #include #include +#include #include #include "devif/devif.h" #include "socket/socket.h" +#ifdef CONFIG_NET_CAN_NOTIFIER +# include +#endif + #ifdef CONFIG_NET_CAN /**************************************************************************** @@ -54,6 +59,16 @@ * 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 */ + struct pollfd *fds; /* Needed to handle poll events */ + 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 @@ -70,16 +85,26 @@ struct can_conn_s FAR struct devif_callback_s *list; /* NetLink callbacks */ 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 */ /* CAN-specific content follows */ uint8_t protocol; /* Selected CAN protocol */ int16_t crefs; /* Reference count */ + - /* poll() support */ + /* The following is a list of poll structures of threads waiting for + * socket events. + */ - FAR sem_t *pollsem; /* Used to wakeup poll() */ - FAR pollevent_t *pollevent; /* poll() wakeup event */ + struct can_poll_s pollinfo[4]; //FIXME make dynamic }; /**************************************************************************** @@ -166,6 +191,35 @@ uint16_t can_callback(FAR struct net_driver_s *dev, FAR struct can_conn_s *conn, uint16_t flags); /**************************************************************************** + * 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: + * conn - A pointer to the CAN connection structure + * buffer - A pointer to the buffer to be copied to the read-ahead + * buffers + * buflen - The number of bytes to copy to the read-ahead buffer. + * + * Returned Value: + * The number of bytes actually buffered is returned. This will be either + * zero or equal to buflen; partial packets are not buffered. + * + * 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 + * + ****************************************************************************/ + +uint16_t can_datahandler(FAR struct can_conn_s *conn, FAR uint8_t *buffer, + uint16_t buflen); + +/**************************************************************************** * Name: can_recvfrom * * Description: @@ -214,17 +268,6 @@ ssize_t can_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, void can_poll(FAR struct net_driver_s *dev, FAR struct can_conn_s *conn); /**************************************************************************** - * Name: can_active() - * - * Description: - * Find a connection structure that is the appropriate connection for the - * provided NetLink address - * - ****************************************************************************/ - -FAR struct can_conn_s *can_active(FAR struct sockaddr_can *addr); - -/**************************************************************************** * Name: psock_can_send * * Description: @@ -247,6 +290,31 @@ struct socket; ssize_t psock_can_send(FAR struct socket *psock, FAR const void *buf, size_t len); +/**************************************************************************** + * 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 + #undef EXTERN #ifdef __cplusplus diff --git a/net/can/can_callback.c b/net/can/can_callback.c index 2fad951..6f3ae93 100644 --- a/net/can/can_callback.c +++ b/net/can/can_callback.c @@ -50,6 +50,61 @@ #include "can/can.h" /**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: can_data_event + * + * Description: + * Handle data that is not accepted by the application because there is no + * listener in place ready to receive the data. + * + * Assumptions: + * - The caller has checked that CAN_NEWDATA is set in flags and that is no + * other handler available to process the incoming data. + * - This function must be called with the network locked. + * + ****************************************************************************/ + +static inline uint16_t +can_data_event(FAR struct net_driver_s *dev, FAR struct can_conn_s *conn, + uint16_t flags) +{ + uint16_t ret; + uint8_t *buffer = dev->d_appdata; + int buflen = dev->d_len; + uint16_t recvlen; + + ret = (flags & ~CAN_NEWDATA); + + //ninfo("No listener on connection\n"); + + /* Save as the packet data as in the read-ahead buffer. NOTE that + * partial packets will not be buffered. + */ + + recvlen = can_datahandler(conn, buffer, buflen); + if (recvlen < buflen) + { + /* There is no handler to receive new data and there are no free + * read-ahead buffers to retain the data -- drop the packet. + */ + + ninfo("Dropped %d bytes\n", dev->d_len); + +#ifdef CONFIG_NET_STATISTICS + //g_netstats.tcp.drop++; +#endif + } + + /* In any event, the new data has now been handled */ + + dev->d_len = 0; + return ret; +} + +/**************************************************************************** * Public Functions ****************************************************************************/ @@ -77,9 +132,97 @@ uint16_t can_callback(FAR struct net_driver_s *dev, /* Perform the callback */ flags = devif_conn_event(dev, conn, flags, conn->list); - } + + if ((flags & CAN_NEWDATA) != 0) + { + /* Data was not handled.. dispose of it appropriately */ + flags = can_data_event(dev, conn, flags); + } + } + return flags; } +/**************************************************************************** + * 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: + * conn - A pointer to the CAN connection structure + * buffer - A pointer to the buffer to be copied to the read-ahead + * buffers + * buflen - The number of bytes to copy to the read-ahead buffer. + * + * Returned Value: + * The number of bytes actually buffered is returned. This will be either + * zero or equal to buflen; partial packets are not buffered. + * + * Assumptions: + * - The caller has checked that CAN_NEWDATA is set in flags and that is no + * other handler available to process the incoming data. + * - This function must be called with the network locked. + * + ****************************************************************************/ + +uint16_t can_datahandler(FAR struct can_conn_s *conn, FAR uint8_t *buffer, + uint16_t buflen) +{ + FAR struct iob_s *iob; + int ret; + + /* Try to allocate on I/O buffer to start the chain without waiting (and + * throttling as necessary). If we would have to wait, then drop the + * packet. + */ + + iob = iob_tryalloc(true, IOBUSER_NET_CAN_READAHEAD); + if (iob == NULL) + { + nerr("ERROR: Failed to create new I/O buffer chain\n"); + return 0; + } + + /* Copy the new appdata into the I/O buffer chain (without waiting) */ + + ret = iob_trycopyin(iob, buffer, buflen, 0, true, + IOBUSER_NET_CAN_READAHEAD); + if (ret < 0) + { + /* On a failure, iob_copyin return a negated error value but does + * not free any I/O buffers. + */ + + nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret); + iob_free_chain(iob, IOBUSER_NET_CAN_READAHEAD); + return 0; + } + + /* Add the new I/O buffer chain to the tail of the read-ahead queue (again + * without waiting). + */ + + ret = iob_tryadd_queue(iob, &conn->readahead); + if (ret < 0) + { + nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret); + iob_free_chain(iob, IOBUSER_NET_TCP_READAHEAD); + return 0; + } + +#ifdef CONFIG_NET_CAN_NOTIFIER + /* Provide notification(s) that additional CAN read-ahead data is + * available. + */ + + can_readahead_signal(conn); +#endif + return buflen; +} + #endif /* CONFIG_NET && CONFIG_NET_CAN */ diff --git a/net/can/can_conn.c b/net/can/can_conn.c index 77733b3..4969f6d 100644 --- a/net/can/can_conn.c +++ b/net/can/can_conn.c @@ -81,6 +81,7 @@ static void _can_semgive(FAR sem_t *sem) { nxsem_post(sem); } + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -201,25 +202,4 @@ FAR struct can_conn_s *can_nextconn(FAR struct can_conn_s *conn) } } -/**************************************************************************** - * Name: can_active - * - * Description: - * Find a connection structure that is the appropriate connection for the - * provided NetLink address - * - * Assumptions: - * - ****************************************************************************/ - -FAR struct can_conn_s *can_active(FAR struct sockaddr_can *addr) -{ - /* This function is used to handle routing of incoming messages to sockets - * connected to the address. There is no such use case for NetLink - * sockets. - */ - - return NULL; -} - #endif /* CONFIG_NET_CAN */ diff --git a/net/can/can_getsockopt.c b/net/can/can_getsockopt.c new file mode 100644 index 0000000..d9e19f8 --- /dev/null +++ b/net/can/can_getsockopt.c @@ -0,0 +1,154 @@ +/**************************************************************************** + * net/can/can_setsockopt.c + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "socket/socket.h" +#include "utils/utils.h" +#include "can/can.h" + +#ifdef CONFIG_NET_CANPROTO_OPTIONS + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * 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 a complete list of values for the socket-level + * 'option' argument. Protocol-specific options are are protocol specific + * header files (such as netpacket/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. + * + ****************************************************************************/ + +int can_getsockopt (FAR struct socket *psock, int option, + FAR void *value, FAR socklen_t *value_len) +{ + + FAR struct can_conn_s *conn; + int ret; + int count = 0; + + DEBUGASSERT(psock != NULL && value != NULL && value_len != NULL && + psock->s_conn != NULL); + conn = (FAR struct can_conn_s *)psock->s_conn; + + if (psock->s_type != SOCK_RAW) + { + nerr("ERROR: Not a RAW CAN socket\n"); + return -ENOTCONN; + } + + + switch (option) + { + + case CAN_RAW_FILTER: + if (*value_len % sizeof(struct can_filter) != 0) + { + ret = -EINVAL; + } + + if (value_len > CAN_RAW_FILTER_MAX * sizeof(struct can_filter)) + { + ret = -EINVAL; + } + + count = *value_len / sizeof(struct can_filter); + + /* FIXME pass filter to driver */ + break; + + case CAN_RAW_ERR_FILTER: + break; + + case CAN_RAW_LOOPBACK: + break; + + case CAN_RAW_RECV_OWN_MSGS: + break; + + case CAN_RAW_FD_FRAMES: + break; + + case CAN_RAW_JOIN_FILTERS: + break; + + default: + nerr("ERROR: Unrecognized RAW CAN socket option: %d\n", option); + ret = -ENOPROTOOPT; + break; + } + + return ret; +} + +#endif /* CONFIG_NET_CANPROTO_OPTIONS */ diff --git a/net/can/can_callback.c b/net/can/can_notifier.c similarity index 70% copy from net/can/can_callback.c copy to net/can/can_notifier.c index 2fad951..ffb4878 100644 --- a/net/can/can_callback.c +++ b/net/can/can_notifier.c @@ -1,7 +1,7 @@ /**************************************************************************** - * net/pkt/pkt_callback.c + * net/can/can_notifier.c * - * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Copyright (C) 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -38,48 +38,46 @@ ****************************************************************************/ #include -#if defined(CONFIG_NET) && defined(CONFIG_NET_CAN) -#include -#include +#include +#include -#include -#include +#include -#include "devif/devif.h" #include "can/can.h" +#ifdef CONFIG_NET_CAN_NOTIFIER + /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** - * Name: can_callback + * Name: can_readahead_signal * * Description: - * Inform the application holding the packet socket of a change in state. + * Read-ahead data has been buffered. Signal all threads waiting for + * read-ahead data to become available. * - * Returned Value: - * OK if packet has been processed, otherwise ERROR. + * 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. * - * Assumptions: - * This function is called with the network locked. + * Returned Value: + * None. * ****************************************************************************/ -uint16_t can_callback(FAR struct net_driver_s *dev, - FAR struct can_conn_s *conn, uint16_t flags) +void can_readahead_signal(FAR struct can_conn_s *conn) { - /* Some sanity checking */ - - if (conn) - { - /* Perform the callback */ - - flags = devif_conn_event(dev, conn, flags, conn->list); - } + /* This is just a simple wrapper around work_notifier_signal(). */ - return flags; + work_notifier_signal(WORK_CAN_READAHEAD, conn); } -#endif /* CONFIG_NET && CONFIG_NET_CAN */ +#endif /* CONFIG_NET_TCP_NOTIFIER */ diff --git a/net/can/can_poll.c b/net/can/can_poll.c index 84aeeab..80e59a2 100644 --- a/net/can/can_poll.c +++ b/net/can/can_poll.c @@ -88,13 +88,13 @@ void can_poll(FAR struct net_driver_s *dev, FAR struct can_conn_s *conn) dev->d_sndlen = 0; /* Perform the application callback */ - can_callback(dev, conn, CAN_POLL); /* Check if the application has data to send */ if (dev->d_sndlen > 0) { + //FIXME missing logic return; } } diff --git a/net/can/can_recvfrom.c b/net/can/can_recvfrom.c index 4062ed3..990786d 100644 --- a/net/can/can_recvfrom.c +++ b/net/can/can_recvfrom.c @@ -68,6 +68,7 @@ struct can_recvfrom_s { + FAR struct socket *pr_sock; /* The parent socket structure */ FAR struct devif_callback_s *pr_cb; /* Reference to callback instance */ sem_t pr_sem; /* Semaphore signals recv completion */ size_t pr_buflen; /* Length of receive buffer */ @@ -128,7 +129,7 @@ static inline void can_add_recvlen(FAR struct can_recvfrom_s *pstate, * ****************************************************************************/ -static void can_recvfrom_newdata(FAR struct net_driver_s *dev, +static size_t can_recvfrom_newdata(FAR struct net_driver_s *dev, FAR struct can_recvfrom_s *pstate) { size_t recvlen; @@ -150,6 +151,150 @@ static void can_recvfrom_newdata(FAR struct net_driver_s *dev, /* Update the accumulated size of the data read */ can_add_recvlen(pstate, recvlen); + + return recvlen; +} + + +/**************************************************************************** + * Name: can_newdata + * + * Description: + * Copy the read data from the packet + * + * Input Parameters: + * dev The structure of the network driver that generated the event + * pstate recvfrom state structure + * + * Returned Value: + * None. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static inline void can_newdata(FAR struct net_driver_s *dev, + FAR struct can_recvfrom_s *pstate) +{ + /* Take as much data from the packet as we can */ + + size_t recvlen = can_recvfrom_newdata(dev, pstate); + + /* If there is more data left in the packet that we could not buffer, then + * add it to the read-ahead buffers. + */ + + if (recvlen < dev->d_len) + { + FAR struct can_conn_s *conn = (FAR struct can_conn_s *)pstate->pr_sock->s_conn; + FAR uint8_t *buffer = (FAR uint8_t *)dev->d_appdata + recvlen; + uint16_t buflen = dev->d_len - recvlen; +#ifdef CONFIG_DEBUG_NET + uint16_t nsaved; + + nsaved = can_datahandler(conn, buffer, buflen); +#else + can_datahandler(conn, buffer, buflen); +#endif + + /* There are complicated buffering issues that are not addressed fully + * here. For example, what if up_datahandler() cannot buffer the + * remainder of the packet? In that case, the data will be dropped but + * still ACKed. Therefore it would not be resent. + * + * This is probably not an issue here because we only get here if the + * read-ahead buffers are empty and there would have to be something + * serioulsy wrong with the configuration not to be able to buffer a + * partial packet in this context. + */ + +#ifdef CONFIG_DEBUG_NET + if (nsaved < buflen) + { + nerr("ERROR: packet data not saved (%d bytes)\n", buflen - nsaved); + } +#endif + } + + /* Indicate no data in the buffer */ + + dev->d_len = 0; +} + +/**************************************************************************** + * Name: can_readahead + * + * Description: + * Copy the read-ahead data from the packet + * + * Input Parameters: + * pstate recvfrom state structure + * + * Returned Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static inline int can_readahead(struct can_recvfrom_s *pstate) +{ + FAR struct can_conn_s *conn = (FAR struct can_conn_s *)pstate->pr_sock->s_conn; + FAR struct iob_s *iob; + int recvlen; + + /* Check there is any CAN data already buffered in a read-ahead + * buffer. + */ + + if((iob = iob_peek_queue(&conn->readahead)) != NULL && + pstate->pr_buflen > 0) + { + DEBUGASSERT(iob->io_pktlen > 0); + + /* Transfer that buffered data from the I/O buffer chain into + * the user buffer. + */ + + recvlen = iob_copyout(pstate->pr_buffer, iob, pstate->pr_buflen, 0); + + /* If we took all of the data from the I/O buffer chain is empty, then + * release it. If there is still data available in the I/O buffer + * chain, then just trim the data that we have taken from the + * beginning of the I/O buffer chain. + */ + + if (recvlen >= iob->io_pktlen) + { + FAR struct iob_s *tmp; + + /* Remove the I/O buffer chain from the head of the read-ahead + * buffer queue. + */ + + tmp = iob_remove_queue(&conn->readahead); + DEBUGASSERT(tmp == iob); + UNUSED(tmp); + + /* And free the I/O buffer chain */ + + iob_free_chain(iob, IOBUSER_NET_CAN_READAHEAD); + } + else + { + /* The bytes that we have received from the head of the I/O + * buffer chain (probably changing the head of the I/O + * buffer queue). + */ + + iob_trimhead_queue(&conn->readahead, recvlen, + IOBUSER_NET_CAN_READAHEAD); + } + return recvlen; + } + return 0; } static uint16_t can_recvfrom_eventhandler(FAR struct net_driver_s *dev, @@ -170,7 +315,7 @@ static uint16_t can_recvfrom_eventhandler(FAR struct net_driver_s *dev, { /* Copy the packet */ - can_recvfrom_newdata(dev, pstate); + can_newdata(dev, pstate); /* We are finished. */ @@ -228,7 +373,7 @@ static ssize_t can_recvfrom_result(int result, if (pstate->pr_result < 0) { /* This might return EAGAIN on a timeout or ENOTCONN on loss of - * connection (TCP only) + * connection (CAN only) */ return pstate->pr_result; @@ -307,6 +452,19 @@ ssize_t can_recvfrom(FAR struct socket *psock, FAR void *buf, state.pr_buflen = len; state.pr_buffer = buf; + state.pr_sock = psock; + + /* Handle any any CAN data already buffered in a read-ahead buffer. NOTE + * that there may be read-ahead data to be retrieved even after the + * socket has been disconnected. + */ + ret = can_readahead(&state); + if(ret > 0) + { + net_unlock(); + nxsem_destroy(&state.pr_sem); + return ret; + } /* Get the device driver that will service this transfer */ @@ -317,7 +475,7 @@ ssize_t can_recvfrom(FAR struct socket *psock, FAR void *buf, goto errout_with_state; } - /* Set up the callback in the connection */ + /* Set up the callback in the connection */ state.pr_cb = can_callback_alloc(dev, conn); if (state.pr_cb) diff --git a/net/can/can_callback.c b/net/can/can_setsockopt.c similarity index 51% copy from net/can/can_callback.c copy to net/can/can_setsockopt.c index 2fad951..db4cb76 100644 --- a/net/can/can_callback.c +++ b/net/can/can_setsockopt.c @@ -1,7 +1,7 @@ /**************************************************************************** - * net/pkt/pkt_callback.c + * net/can/can_setsockopt.c * - * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Copyright (C) 2018, 2020 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -38,48 +38,110 @@ ****************************************************************************/ #include -#if defined(CONFIG_NET) && defined(CONFIG_NET_CAN) +#include #include +#include +#include #include -#include -#include +#include -#include "devif/devif.h" +#include +#include + +#include "socket/socket.h" +#include "utils/utils.h" #include "can/can.h" +#ifdef CONFIG_NET_CANPROTO_OPTIONS + /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** - * Name: can_callback + * Name: can_setsockopt * * Description: - * Inform the application holding the packet socket of a change in state. + * 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. * - * Returned Value: - * OK if packet has been processed, otherwise ERROR. + * See for the a complete list of values of CAN protocol + * options. * - * Assumptions: - * This function is called with the network locked. + * Input Parameters: + * psock Socket structure of socket to operate on + * 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. * ****************************************************************************/ -uint16_t can_callback(FAR struct net_driver_s *dev, - FAR struct can_conn_s *conn, uint16_t flags) +int can_setsockopt(FAR struct socket *psock, int option, + FAR const void *value, socklen_t value_len) { - /* Some sanity checking */ + + FAR struct can_conn_s *conn; + int ret; + int count = 0; + + DEBUGASSERT(psock != NULL && value != NULL && psock->s_conn != NULL); + conn = (FAR struct can_conn_s *)psock->s_conn; - if (conn) + if (psock->s_type != SOCK_RAW) { - /* Perform the callback */ + nerr("ERROR: Not a RAW CAN socket\n"); + return -ENOTCONN; + } + + + switch (option) + { + case CAN_RAW_FILTER: + if (value_len % sizeof(struct can_filter) != 0) + { + ret = -EINVAL; + } + + if (value_len > CAN_RAW_FILTER_MAX * sizeof(struct can_filter)) + { + ret = -EINVAL; + } + + count = value_len / sizeof(struct can_filter); + + /* FIXME pass filter to driver */ + break; + + case CAN_RAW_ERR_FILTER: + break; + + case CAN_RAW_LOOPBACK: + break; + + case CAN_RAW_RECV_OWN_MSGS: + break; + + case CAN_RAW_FD_FRAMES: + break; + + case CAN_RAW_JOIN_FILTERS: + break; - flags = devif_conn_event(dev, conn, flags, conn->list); + default: + nerr("ERROR: Unrecognized CAN option: %d\n", option); + ret = -ENOPROTOOPT; + break; } - return flags; + return ret; } -#endif /* CONFIG_NET && CONFIG_NET_CAN */ +#endif /* CONFIG_NET_CANPROTO_OPTIONS */ diff --git a/net/can/can_sockif.c b/net/can/can_sockif.c index 0cd389a..5c8415e 100644 --- a/net/can/can_sockif.c +++ b/net/can/can_sockif.c @@ -86,7 +86,7 @@ const struct sock_intf_s g_can_sockif = can_listen, /* si_listen */ can_connect, /* si_connect */ can_accept, /* si_accept */ - can_poll_local, /* si_poll */ + can_poll_local, /* si_poll */ can_send, /* si_send */ can_sendto, /* si_sendto */ #ifdef CONFIG_NET_SENDFILE @@ -102,6 +102,73 @@ const struct sock_intf_s g_can_sockif = ****************************************************************************/ /**************************************************************************** + * Name: can_poll_eventhandler + * + * Description: + * This function is called to perform the actual CAN receive operation + * via the device interface layer. + * + * Input Parameters: + * dev The structure of the network driver that caused the event + * conn The connection structure associated with the socket + * flags Set of events describing why the callback was invoked + * + * Returned Value: + * None + * + * Assumptions: + * This function must be called with the network locked. + * + ****************************************************************************/ + +static uint16_t can_poll_eventhandler(FAR struct net_driver_s *dev, + FAR void *conn, + FAR void *pvpriv, uint16_t flags) +{ + FAR struct can_poll_s *info = (FAR struct can_poll_s *)pvpriv; + + DEBUGASSERT(!info || (info->psock && info->fds)); + + /* 'priv' might be null in some race conditions (?) */ + + if (info) + { + pollevent_t eventset = 0; + + /* Check for data or connection availability events. */ + + if ((flags & CAN_NEWDATA) != 0) + { + eventset |= (POLLIN & info->fds->events); + } + + /* Check for loss of connection events. */ + + if ((flags & NETDEV_DOWN) != 0) + { + eventset |= (POLLHUP | POLLERR); + } + + /* A poll is a sign that we are free to send data. */ + + /* else if ((flags & CAN_POLL) != 0 && psock_udp_cansend(info->psock) >= 0) + { + eventset |= (POLLOUT & info->fds->events); + }*/ + + /* Awaken the caller of poll() is requested event occurred. */ + + if (eventset) + { + info->fds->revents |= eventset; + nxsem_post(info->fds->sem); + } + } + + return flags; +} + +/**************************************************************************** * Name: can_setup * * Description: @@ -506,100 +573,109 @@ static int can_poll_local(FAR struct socket *psock, FAR struct pollfd *fds, bool setup) { FAR struct can_conn_s *conn; - int ret; + FAR struct can_poll_s *info; + FAR struct devif_callback_s *cb; + int ret = OK; DEBUGASSERT(psock != NULL && psock->s_conn != NULL); conn = (FAR struct can_conn_s *)psock->s_conn; + info = conn->pollinfo; + + //FIXME add NETDEV_DOWN support /* Check if we are setting up or tearing down the poll */ if (setup) { - /* If POLLOUT is selected, return immediately (maybe) */ - - pollevent_t revents = POLLOUT; - - /* If POLLIN is selected and a response is available, return - * immediately if POLLIN and/or POLLIN are included in the - * requested event set. - */ - + net_lock(); - -#warning Missing logic - - revents &= fds->events; - if (revents != 0) + + info->dev = conn->dev; + + cb = can_callback_alloc(info->dev, conn); + if (cb == NULL) { - fds->revents = revents; - nxsem_post(fds->sem); - net_unlock(); - return OK; + ret = -EBUSY; + goto errout_with_lock; } - /* Set up to be notified when a response is available if POLLIN is - * requested. + /* Initialize the poll info container */ + + info->psock = psock; + info->fds = fds; + info->cb = cb; + + /* Initialize the callback structure. Save the reference to the info + * structure as callback private data so that it will be available during + * callback processing. */ - + + cb->flags = NETDEV_DOWN; + cb->priv = (FAR void *)info; + cb->event = can_poll_eventhandler; + + if ((fds->events & POLLOUT) != 0) + { + cb->flags |= CAN_POLL; + } + if ((fds->events & POLLIN) != 0) { - /* Some limitations: There can be only a single outstanding POLLIN - * on the CAN connection. - */ - - if (conn->pollsem != NULL || conn->pollevent != NULL) - { - nerr("ERROR: Multiple polls() on socket not supported.\n"); - net_unlock(); - return -EBUSY; - } - - /* Set up the notification */ - - conn->pollsem = fds->sem; - conn->pollevent = &fds->revents; - -#warning Missing logic - - if (ret < 0) - { - /* Failed to set up notification */ - - conn->pollsem = NULL; - conn->pollevent = NULL; - } - else - { - /* Setup to receive a notification when CAN data is available */ - -#warning Missing logic - - ret = OK; - } + cb->flags |= CAN_NEWDATA; } - - /* Set up to be notified when we are able to send CAN data without - * waiting. + + /* Save the reference in the poll info structure as fds private as well + * for use during poll teardown as well. */ - - else if ((fds->events & POLLOUT) != 0) + + fds->priv = (FAR void *)info; + + /* Check for read data availability now */ + + if (!IOB_QEMPTY(&conn->readahead)) { + /* Normal data may be read without blocking. */ + + fds->revents |= (POLLRDNORM & fds->events); } - else + + #if 0 + if (psock_udp_cansend(psock) >= 0) { - /* There will not be any wakeups coming? Probably an error? */ - - ret = OK; + /* Normal data may be sent without blocking (at least one byte). */ + + fds->revents |= (POLLWRNORM & fds->events); } - + #endif + + /* Check if any requested events are already in effect */ + + if (fds->revents != 0) + { + /* Yes.. then signal the poll logic */ + nxsem_post(fds->sem); + } + +errout_with_lock: net_unlock(); } - else + else { - /* Cancel any response notifications */ + info = (FAR struct can_poll_s *)fds->priv; + + if (info != NULL) + { + /* Cancel any response notifications */ + can_callback_free(info->dev, conn, info->cb); + + /* Release the poll/select data slot */ + + info->fds->priv = NULL; + + /* Then free the poll info container */ - conn->pollsem = NULL; - conn->pollevent = NULL; + info->psock = NULL; + } } return ret; diff --git a/net/socket/Kconfig b/net/socket/Kconfig index c627ea5..8b71ade 100644 --- a/net/socket/Kconfig +++ b/net/socket/Kconfig @@ -37,6 +37,12 @@ config NET_UDPPROTO_OPTIONS ---help--- Enable or disable support for UDP protocol level socket options. +config NET_CANPROTO_OPTIONS + bool + default n + ---help--- + Enable or disable support for CAN protocol level socket option + if NET_SOCKOPTS config NET_SOLINGER diff --git a/net/socket/getsockopt.c b/net/socket/getsockopt.c index 2bc1c73..f28c80b 100644 --- a/net/socket/getsockopt.c +++ b/net/socket/getsockopt.c @@ -367,6 +367,12 @@ int psock_getsockopt(FAR struct socket *psock, int level, int option, break; #endif + case SOL_CAN_RAW: +#ifdef CONFIG_NET_TCPPROTO_OPTIONS + ret = can_getsockopt(psock, option, value, value_len); +#endif + break; + /* These levels are defined in sys/socket.h, but are not yet * implemented. */