You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by gn...@apache.org on 2020/06/02 14:09:25 UTC
[incubator-nuttx] 07/31: Added basic poll()/select support
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 b76d6099a570a5ff08893155e79d61c3ffb66d7c
Author: Peter van der Perk <pe...@nxp.com>
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 <nuttx/wqueue.h>
#endif
+#include <queue.h>
+
#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
+ *
+ * <received_can_id> & 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 d353772..5ea8dfc 100644
--- a/include/nuttx/wqueue.h
+++ b/include/nuttx/wqueue.h
@@ -280,7 +280,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 e85242e..bc7cd26 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 <netpacket/can.h>
#include <nuttx/semaphore.h>
+#include <nuttx/can.h>
#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
/****************************************************************************
@@ -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 <gn...@nuttx.org>
+ *
+ * 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 <nuttx/config.h>
+
+#include <sys/time.h>
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <netpacket/can.h>
+
+#include <nuttx/net/net.h>
+#include <nuttx/net/can.h>
+
+#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 <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 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 <gn...@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -38,48 +38,46 @@
****************************************************************************/
#include <nuttx/config.h>
-#if defined(CONFIG_NET) && defined(CONFIG_NET_CAN)
-#include <stdint.h>
-#include <debug.h>
+#include <sys/types.h>
+#include <assert.h>
-#include <nuttx/net/netconfig.h>
-#include <nuttx/net/netdev.h>
+#include <nuttx/wqueue.h>
-#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 <gn...@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -38,48 +38,110 @@
****************************************************************************/
#include <nuttx/config.h>
-#if defined(CONFIG_NET) && defined(CONFIG_NET_CAN)
+#include <sys/time.h>
#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
#include <debug.h>
-#include <nuttx/net/netconfig.h>
-#include <nuttx/net/netdev.h>
+#include <netpacket/can.h>
-#include "devif/devif.h"
+#include <nuttx/net/net.h>
+#include <nuttx/net/can.h>
+
+#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 <netinet/can.h> 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 e7474ad..bc07a03 100644
--- a/net/socket/getsockopt.c
+++ b/net/socket/getsockopt.c
@@ -371,6 +371,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.
*/