From 38fa644069d2a40cef8fe1df876588a5a76845cf Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 9 Apr 2019 09:35:40 +0200 Subject: [PATCH 1/7] include: import header files from linux-5.0 (including new j1939 filter struct) Signed-off-by: Oleksij Rempel --- include/linux/can/j1939.h | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/include/linux/can/j1939.h b/include/linux/can/j1939.h index cd60d70..603ce13 100644 --- a/include/linux/can/j1939.h +++ b/include/linux/can/j1939.h @@ -16,12 +16,13 @@ #include #include -#define J1939_IDLE_ADDR 0xfe #define J1939_MAX_UNICAST_ADDR 0xfd +#define J1939_IDLE_ADDR 0xfe #define J1939_NO_ADDR 0xff /* == broadcast or no addr */ #define J1939_NO_NAME 0 -#define J1939_PGN_REQUEST 0x0ea00 -#define J1939_PGN_ADDRESS_CLAIMED 0x0ee00 +#define J1939_PGN_REQUEST 0x0ea00 /* Request PG */ +#define J1939_PGN_ADDRESS_CLAIMED 0x0ee00 /* Address Claimed */ +#define J1939_PGN_ADDRESS_COMMANDED 0x0fed8 /* Commanded Address */ #define J1939_PGN_PDU1_MAX 0x3ff00 #define J1939_PGN_MAX 0x3ffff #define J1939_NO_PGN 0x40000 @@ -65,21 +66,33 @@ enum { SO_J1939_PROMISC = 2, /* set/clr promiscuous mode */ SO_J1939_RECV_OWN = 3, SO_J1939_SEND_PRIO = 4, + SO_J1939_ERRQUEUE = 5, }; enum { SCM_J1939_DEST_ADDR = 1, SCM_J1939_DEST_NAME = 2, SCM_J1939_PRIO = 3, + SCM_J1939_ERRQUEUE = 4, +}; + +enum { + J1939_NLA_PAD, + J1939_NLA_BYTES_ACKED, +}; + +enum { + J1939_EE_INFO_NONE, + J1939_EE_INFO_TX_ABORT, }; struct j1939_filter { name_t name; name_t name_mask; - __u8 addr; - __u8 addr_mask; pgn_t pgn; pgn_t pgn_mask; + __u8 addr; + __u8 addr_mask; }; #define J1939_FILTER_MAX 512 /* maximum number of j1939_filter set via setsockopt() */ From 9ff370ea8218c0e8b11ca188c01cdb52dac4fca4 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 7 May 2019 10:18:16 +0200 Subject: [PATCH 2/7] include: add errqueue.h, net_tstamp.h, netlink.h this headers are take from kernel v5.0 and needed for error queue support. Signed-off-by: Oleksij Rempel --- GNUmakefile.am | 8 +- include/linux/errqueue.h | 54 ++++++++ include/linux/net_tstamp.h | 162 ++++++++++++++++++++++++ include/linux/netlink.h | 252 +++++++++++++++++++++++++++++++++++++ 4 files changed, 473 insertions(+), 3 deletions(-) create mode 100644 include/linux/errqueue.h create mode 100644 include/linux/net_tstamp.h create mode 100644 include/linux/netlink.h diff --git a/GNUmakefile.am b/GNUmakefile.am index 8961cdb..013c8ed 100644 --- a/GNUmakefile.am +++ b/GNUmakefile.am @@ -14,16 +14,18 @@ noinst_HEADERS = \ lib.h \ libj1939.h \ terminal.h \ + include/linux/can.h \ include/linux/can/bcm.h \ include/linux/can/error.h \ include/linux/can/gw.h \ - include/linux/can.h \ include/linux/can/isotp.h \ include/linux/can/j1939.h \ include/linux/can/netlink.h \ include/linux/can/raw.h \ - include/linux/can/vxcan.h - + include/linux/can/vxcan.h \ + include/linux/errqueue.h \ + include/linux/net_tstamp.h \ + include/linux/netlink.h noinst_LTLIBRARIES = \ libcan.la \ diff --git a/include/linux/errqueue.h b/include/linux/errqueue.h new file mode 100644 index 0000000..c015120 --- /dev/null +++ b/include/linux/errqueue.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_LINUX_ERRQUEUE_H +#define _UAPI_LINUX_ERRQUEUE_H + +#include + +struct sock_extended_err { + __u32 ee_errno; + __u8 ee_origin; + __u8 ee_type; + __u8 ee_code; + __u8 ee_pad; + __u32 ee_info; + __u32 ee_data; +}; + +#define SO_EE_ORIGIN_NONE 0 +#define SO_EE_ORIGIN_LOCAL 1 +#define SO_EE_ORIGIN_ICMP 2 +#define SO_EE_ORIGIN_ICMP6 3 +#define SO_EE_ORIGIN_TXSTATUS 4 +#define SO_EE_ORIGIN_ZEROCOPY 5 +#define SO_EE_ORIGIN_TXTIME 6 +#define SO_EE_ORIGIN_TIMESTAMPING SO_EE_ORIGIN_TXSTATUS + +#define SO_EE_OFFENDER(ee) ((struct sockaddr*)((ee)+1)) + +#define SO_EE_CODE_ZEROCOPY_COPIED 1 + +#define SO_EE_CODE_TXTIME_INVALID_PARAM 1 +#define SO_EE_CODE_TXTIME_MISSED 2 + +/** + * struct scm_timestamping - timestamps exposed through cmsg + * + * The timestamping interfaces SO_TIMESTAMPING, MSG_TSTAMP_* + * communicate network timestamps by passing this struct in a cmsg with + * recvmsg(). See Documentation/networking/timestamping.txt for details. + */ +struct scm_timestamping { + struct timespec ts[3]; +}; + +/* The type of scm_timestamping, passed in sock_extended_err ee_info. + * This defines the type of ts[0]. For SCM_TSTAMP_SND only, if ts[0] + * is zero, then this is a hardware timestamp and recorded in ts[2]. + */ +enum { + SCM_TSTAMP_SND, /* driver passed skb to NIC, or HW */ + SCM_TSTAMP_SCHED, /* data entered the packet scheduler */ + SCM_TSTAMP_ACK, /* data acknowledged by peer */ +}; + +#endif /* _UAPI_LINUX_ERRQUEUE_H */ diff --git a/include/linux/net_tstamp.h b/include/linux/net_tstamp.h new file mode 100644 index 0000000..e5b3972 --- /dev/null +++ b/include/linux/net_tstamp.h @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Userspace API for hardware time stamping of network packets + * + * Copyright (C) 2008,2009 Intel Corporation + * Author: Patrick Ohly + * + */ + +#ifndef _NET_TIMESTAMPING_H +#define _NET_TIMESTAMPING_H + +#include +#include /* for SO_TIMESTAMPING */ + +/* SO_TIMESTAMPING gets an integer bit field comprised of these values */ +enum { + SOF_TIMESTAMPING_TX_HARDWARE = (1<<0), + SOF_TIMESTAMPING_TX_SOFTWARE = (1<<1), + SOF_TIMESTAMPING_RX_HARDWARE = (1<<2), + SOF_TIMESTAMPING_RX_SOFTWARE = (1<<3), + SOF_TIMESTAMPING_SOFTWARE = (1<<4), + SOF_TIMESTAMPING_SYS_HARDWARE = (1<<5), + SOF_TIMESTAMPING_RAW_HARDWARE = (1<<6), + SOF_TIMESTAMPING_OPT_ID = (1<<7), + SOF_TIMESTAMPING_TX_SCHED = (1<<8), + SOF_TIMESTAMPING_TX_ACK = (1<<9), + SOF_TIMESTAMPING_OPT_CMSG = (1<<10), + SOF_TIMESTAMPING_OPT_TSONLY = (1<<11), + SOF_TIMESTAMPING_OPT_STATS = (1<<12), + SOF_TIMESTAMPING_OPT_PKTINFO = (1<<13), + SOF_TIMESTAMPING_OPT_TX_SWHW = (1<<14), + + SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_OPT_TX_SWHW, + SOF_TIMESTAMPING_MASK = (SOF_TIMESTAMPING_LAST - 1) | + SOF_TIMESTAMPING_LAST +}; + +/* + * SO_TIMESTAMPING flags are either for recording a packet timestamp or for + * reporting the timestamp to user space. + * Recording flags can be set both via socket options and control messages. + */ +#define SOF_TIMESTAMPING_TX_RECORD_MASK (SOF_TIMESTAMPING_TX_HARDWARE | \ + SOF_TIMESTAMPING_TX_SOFTWARE | \ + SOF_TIMESTAMPING_TX_SCHED | \ + SOF_TIMESTAMPING_TX_ACK) + +/** + * struct hwtstamp_config - %SIOCGHWTSTAMP and %SIOCSHWTSTAMP parameter + * + * @flags: no flags defined right now, must be zero for %SIOCSHWTSTAMP + * @tx_type: one of HWTSTAMP_TX_* + * @rx_filter: one of HWTSTAMP_FILTER_* + * + * %SIOCGHWTSTAMP and %SIOCSHWTSTAMP expect a &struct ifreq with a + * ifr_data pointer to this structure. For %SIOCSHWTSTAMP, if the + * driver or hardware does not support the requested @rx_filter value, + * the driver may use a more general filter mode. In this case + * @rx_filter will indicate the actual mode on return. + */ +struct hwtstamp_config { + int flags; + int tx_type; + int rx_filter; +}; + +/* possible values for hwtstamp_config->tx_type */ +enum hwtstamp_tx_types { + /* + * No outgoing packet will need hardware time stamping; + * should a packet arrive which asks for it, no hardware + * time stamping will be done. + */ + HWTSTAMP_TX_OFF, + + /* + * Enables hardware time stamping for outgoing packets; + * the sender of the packet decides which are to be + * time stamped by setting %SOF_TIMESTAMPING_TX_SOFTWARE + * before sending the packet. + */ + HWTSTAMP_TX_ON, + + /* + * Enables time stamping for outgoing packets just as + * HWTSTAMP_TX_ON does, but also enables time stamp insertion + * directly into Sync packets. In this case, transmitted Sync + * packets will not received a time stamp via the socket error + * queue. + */ + HWTSTAMP_TX_ONESTEP_SYNC, +}; + +/* possible values for hwtstamp_config->rx_filter */ +enum hwtstamp_rx_filters { + /* time stamp no incoming packet at all */ + HWTSTAMP_FILTER_NONE, + + /* time stamp any incoming packet */ + HWTSTAMP_FILTER_ALL, + + /* return value: time stamp all packets requested plus some others */ + HWTSTAMP_FILTER_SOME, + + /* PTP v1, UDP, any kind of event packet */ + HWTSTAMP_FILTER_PTP_V1_L4_EVENT, + /* PTP v1, UDP, Sync packet */ + HWTSTAMP_FILTER_PTP_V1_L4_SYNC, + /* PTP v1, UDP, Delay_req packet */ + HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ, + /* PTP v2, UDP, any kind of event packet */ + HWTSTAMP_FILTER_PTP_V2_L4_EVENT, + /* PTP v2, UDP, Sync packet */ + HWTSTAMP_FILTER_PTP_V2_L4_SYNC, + /* PTP v2, UDP, Delay_req packet */ + HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ, + + /* 802.AS1, Ethernet, any kind of event packet */ + HWTSTAMP_FILTER_PTP_V2_L2_EVENT, + /* 802.AS1, Ethernet, Sync packet */ + HWTSTAMP_FILTER_PTP_V2_L2_SYNC, + /* 802.AS1, Ethernet, Delay_req packet */ + HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ, + + /* PTP v2/802.AS1, any layer, any kind of event packet */ + HWTSTAMP_FILTER_PTP_V2_EVENT, + /* PTP v2/802.AS1, any layer, Sync packet */ + HWTSTAMP_FILTER_PTP_V2_SYNC, + /* PTP v2/802.AS1, any layer, Delay_req packet */ + HWTSTAMP_FILTER_PTP_V2_DELAY_REQ, + + /* NTP, UDP, all versions and packet modes */ + HWTSTAMP_FILTER_NTP_ALL, +}; + +/* SCM_TIMESTAMPING_PKTINFO control message */ +struct scm_ts_pktinfo { + __u32 if_index; + __u32 pkt_length; + __u32 reserved[2]; +}; + +/* + * SO_TXTIME gets a struct sock_txtime with flags being an integer bit + * field comprised of these values. + */ +enum txtime_flags { + SOF_TXTIME_DEADLINE_MODE = (1 << 0), + SOF_TXTIME_REPORT_ERRORS = (1 << 1), + + SOF_TXTIME_FLAGS_LAST = SOF_TXTIME_REPORT_ERRORS, + SOF_TXTIME_FLAGS_MASK = (SOF_TXTIME_FLAGS_LAST - 1) | + SOF_TXTIME_FLAGS_LAST +}; + +struct sock_txtime { + __kernel_clockid_t clockid;/* reference clockid */ + __u32 flags; /* as defined by enum txtime_flags */ +}; + +#endif /* _NET_TIMESTAMPING_H */ diff --git a/include/linux/netlink.h b/include/linux/netlink.h new file mode 100644 index 0000000..0a4d733 --- /dev/null +++ b/include/linux/netlink.h @@ -0,0 +1,252 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI__LINUX_NETLINK_H +#define _UAPI__LINUX_NETLINK_H + +#include +#include /* for __kernel_sa_family_t */ +#include + +#define NETLINK_ROUTE 0 /* Routing/device hook */ +#define NETLINK_UNUSED 1 /* Unused number */ +#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ +#define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */ +#define NETLINK_SOCK_DIAG 4 /* socket monitoring */ +#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */ +#define NETLINK_XFRM 6 /* ipsec */ +#define NETLINK_SELINUX 7 /* SELinux event notifications */ +#define NETLINK_ISCSI 8 /* Open-iSCSI */ +#define NETLINK_AUDIT 9 /* auditing */ +#define NETLINK_FIB_LOOKUP 10 +#define NETLINK_CONNECTOR 11 +#define NETLINK_NETFILTER 12 /* netfilter subsystem */ +#define NETLINK_IP6_FW 13 +#define NETLINK_DNRTMSG 14 /* DECnet routing messages */ +#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */ +#define NETLINK_GENERIC 16 +/* leave room for NETLINK_DM (DM Events) */ +#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */ +#define NETLINK_ECRYPTFS 19 +#define NETLINK_RDMA 20 +#define NETLINK_CRYPTO 21 /* Crypto layer */ +#define NETLINK_SMC 22 /* SMC monitoring */ + +#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG + +#define MAX_LINKS 32 + +struct sockaddr_nl { + __kernel_sa_family_t nl_family; /* AF_NETLINK */ + unsigned short nl_pad; /* zero */ + __u32 nl_pid; /* port ID */ + __u32 nl_groups; /* multicast groups mask */ +}; + +struct nlmsghdr { + __u32 nlmsg_len; /* Length of message including header */ + __u16 nlmsg_type; /* Message content */ + __u16 nlmsg_flags; /* Additional flags */ + __u32 nlmsg_seq; /* Sequence number */ + __u32 nlmsg_pid; /* Sending process port ID */ +}; + +/* Flags values */ + +#define NLM_F_REQUEST 0x01 /* It is request message. */ +#define NLM_F_MULTI 0x02 /* Multipart message, terminated by NLMSG_DONE */ +#define NLM_F_ACK 0x04 /* Reply with ack, with zero or error code */ +#define NLM_F_ECHO 0x08 /* Echo this request */ +#define NLM_F_DUMP_INTR 0x10 /* Dump was inconsistent due to sequence change */ +#define NLM_F_DUMP_FILTERED 0x20 /* Dump was filtered as requested */ + +/* Modifiers to GET request */ +#define NLM_F_ROOT 0x100 /* specify tree root */ +#define NLM_F_MATCH 0x200 /* return all matching */ +#define NLM_F_ATOMIC 0x400 /* atomic GET */ +#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) + +/* Modifiers to NEW request */ +#define NLM_F_REPLACE 0x100 /* Override existing */ +#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */ +#define NLM_F_CREATE 0x400 /* Create, if it does not exist */ +#define NLM_F_APPEND 0x800 /* Add to end of list */ + +/* Modifiers to DELETE request */ +#define NLM_F_NONREC 0x100 /* Do not delete recursively */ + +/* Flags for ACK message */ +#define NLM_F_CAPPED 0x100 /* request was capped */ +#define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */ + +/* + 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL + 4.4BSD CHANGE NLM_F_REPLACE + + True CHANGE NLM_F_CREATE|NLM_F_REPLACE + Append NLM_F_CREATE + Check NLM_F_EXCL + */ + +#define NLMSG_ALIGNTO 4U +#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) +#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN) +#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) +#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) +#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ + (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) +#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len <= (len)) +#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len))) + +#define NLMSG_NOOP 0x1 /* Nothing. */ +#define NLMSG_ERROR 0x2 /* Error */ +#define NLMSG_DONE 0x3 /* End of a dump */ +#define NLMSG_OVERRUN 0x4 /* Data lost */ + +#define NLMSG_MIN_TYPE 0x10 /* < 0x10: reserved control messages */ + +struct nlmsgerr { + int error; + struct nlmsghdr msg; + /* + * followed by the message contents unless NETLINK_CAP_ACK was set + * or the ACK indicates success (error == 0) + * message length is aligned with NLMSG_ALIGN() + */ + /* + * followed by TLVs defined in enum nlmsgerr_attrs + * if NETLINK_EXT_ACK was set + */ +}; + +/** + * enum nlmsgerr_attrs - nlmsgerr attributes + * @NLMSGERR_ATTR_UNUSED: unused + * @NLMSGERR_ATTR_MSG: error message string (string) + * @NLMSGERR_ATTR_OFFS: offset of the invalid attribute in the original + * message, counting from the beginning of the header (u32) + * @NLMSGERR_ATTR_COOKIE: arbitrary subsystem specific cookie to + * be used - in the success case - to identify a created + * object or operation or similar (binary) + * @__NLMSGERR_ATTR_MAX: number of attributes + * @NLMSGERR_ATTR_MAX: highest attribute number + */ +enum nlmsgerr_attrs { + NLMSGERR_ATTR_UNUSED, + NLMSGERR_ATTR_MSG, + NLMSGERR_ATTR_OFFS, + NLMSGERR_ATTR_COOKIE, + + __NLMSGERR_ATTR_MAX, + NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1 +}; + +#define NETLINK_ADD_MEMBERSHIP 1 +#define NETLINK_DROP_MEMBERSHIP 2 +#define NETLINK_PKTINFO 3 +#define NETLINK_BROADCAST_ERROR 4 +#define NETLINK_NO_ENOBUFS 5 +#ifndef __KERNEL__ +#define NETLINK_RX_RING 6 +#define NETLINK_TX_RING 7 +#endif +#define NETLINK_LISTEN_ALL_NSID 8 +#define NETLINK_LIST_MEMBERSHIPS 9 +#define NETLINK_CAP_ACK 10 +#define NETLINK_EXT_ACK 11 +#define NETLINK_GET_STRICT_CHK 12 + +struct nl_pktinfo { + __u32 group; +}; + +struct nl_mmap_req { + unsigned int nm_block_size; + unsigned int nm_block_nr; + unsigned int nm_frame_size; + unsigned int nm_frame_nr; +}; + +struct nl_mmap_hdr { + unsigned int nm_status; + unsigned int nm_len; + __u32 nm_group; + /* credentials */ + __u32 nm_pid; + __u32 nm_uid; + __u32 nm_gid; +}; + +#ifndef __KERNEL__ +enum nl_mmap_status { + NL_MMAP_STATUS_UNUSED, + NL_MMAP_STATUS_RESERVED, + NL_MMAP_STATUS_VALID, + NL_MMAP_STATUS_COPY, + NL_MMAP_STATUS_SKIP, +}; + +#define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO +#define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT) +#define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr)) +#endif + +#define NET_MAJOR 36 /* Major 36 is reserved for networking */ + +enum { + NETLINK_UNCONNECTED = 0, + NETLINK_CONNECTED, +}; + +/* + * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)--> + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * | Header | Pad | Payload | Pad | + * | (struct nlattr) | ing | | ing | + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * <-------------- nlattr->nla_len --------------> + */ + +struct nlattr { + __u16 nla_len; + __u16 nla_type; +}; + +/* + * nla_type (16 bits) + * +---+---+-------------------------------+ + * | N | O | Attribute Type | + * +---+---+-------------------------------+ + * N := Carries nested attributes + * O := Payload stored in network byte order + * + * Note: The N and O flag are mutually exclusive. + */ +#define NLA_F_NESTED (1 << 15) +#define NLA_F_NET_BYTEORDER (1 << 14) +#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER) + +#define NLA_ALIGNTO 4 +#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) +#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) + +/* Generic 32 bitflags attribute content sent to the kernel. + * + * The value is a bitmap that defines the values being set + * The selector is a bitmask that defines which value is legit + * + * Examples: + * value = 0x0, and selector = 0x1 + * implies we are selecting bit 1 and we want to set its value to 0. + * + * value = 0x2, and selector = 0x2 + * implies we are selecting bit 2 and we want to set its value to 1. + * + */ +struct nla_bitfield32 { + __u32 value; + __u32 selector; +}; + +#endif /* _UAPI__LINUX_NETLINK_H */ From 6260263ef3a21f3071675898572ef34ba6076d9c Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Thu, 9 May 2019 14:01:24 +0200 Subject: [PATCH 3/7] buildsystem: add SCM_TIMESTAMPING_OPT_STATS if not already defined --- CMakeLists.txt | 1 + Makefile | 1 + configure.ac | 3 +++ 3 files changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c3607ef..54717ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSO_RXQ_OVFL=40") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DPF_CAN=29") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DAF_CAN=PF_CAN") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DN_SLCAN=17") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSCM_TIMESTAMPING_OPT_STATS=54") include_directories (.) include_directories (./include) diff --git a/Makefile b/Makefile index 2a0962a..d215b1a 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,7 @@ CPPFLAGS += \ -DAF_CAN=PF_CAN \ -DPF_CAN=29 \ -DSO_RXQ_OVFL=40 \ + -DSCM_TIMESTAMPING_OPT_STATS=54 \ -D_FILE_OFFSET_BITS=64 \ -D_GNU_SOURCE diff --git a/configure.ac b/configure.ac index fdfc300..2d4993b 100644 --- a/configure.ac +++ b/configure.ac @@ -88,6 +88,9 @@ AC_CHECK_DECL(AF_CAN,, AC_CHECK_DECL(N_SLCAN,, [AC_DEFINE([N_SLCAN], [17], [N_SLCAN])] ) +AC_CHECK_DECL(SCM_TIMESTAMPING_OPT_STATS,, + [AC_DEFINE([SCM_TIMESTAMPING_OPT_STATS], [54], [SCM_TIMESTAMPING_OPT_STATS])] +) AC_DEFINE(_GNU_SOURCE) From 05a66d63fa65e486e08c1d3066737ff51a5854d3 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 8 May 2019 11:10:22 +0200 Subject: [PATCH 4/7] jcat: enable polling by default and set more or less meaningful default value. Signed-off-by: Oleksij Rempel --- jcat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/jcat.c b/jcat.c index c299c31..c050c19 100644 --- a/jcat.c +++ b/jcat.c @@ -422,6 +422,7 @@ int main(int argc, char *argv[]) priv->infile = STDIN_FILENO; priv->outfile = STDOUT_FILENO; priv->max_transfer = J1939_MAX_ETP_PACKET_SIZE; + priv->polltimeout = 100000; jcat_init_sockaddr_can(&priv->sockname); jcat_init_sockaddr_can(&priv->peername); From 05368ef1aebf9d339afbd083d3f5671ae3fba3a2 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Wed, 8 May 2019 11:33:26 +0200 Subject: [PATCH 5/7] jcat: rework poller Make poller to be a main part of the send loop. It should make application more understandable. Signed-off-by: Oleksij Rempel --- jcat.c | 65 ++++++++++++++++++++++++++-------------------------------- 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/jcat.c b/jcat.c index c050c19..e813af6 100644 --- a/jcat.c +++ b/jcat.c @@ -111,53 +111,46 @@ static ssize_t jcat_send_one(struct jcat_priv *priv, int out_fd, return num_sent; } -static int jcat_poll(struct jcat_priv *priv) -{ - struct pollfd fds = { - .fd = priv->sock, - .events = POLLOUT, - }; - int ret; - - if (!priv->polltimeout) - return 0; - - ret = poll(&fds, 1, priv->polltimeout); - if (ret < 0) - return -errno; - else if (!ret) { - warn("%s: timeout", __func__); - return -ETIME; - } - - if (!(fds.revents & POLLOUT)) { - warn("%s: something else is wrong", __func__); - return -EIO; - } - - return 0; -} - static int jcat_send_loop(struct jcat_priv *priv, int out_fd, char *buf, size_t buf_size) { - ssize_t count, num_sent; + ssize_t count, num_sent = 0; char *tmp_buf = buf; - int ret; + unsigned int events = POLLOUT; count = buf_size; while (count) { - ret = jcat_poll(priv); - if (ret) { + if (priv->polltimeout) { + struct pollfd fds = { + .fd = priv->sock, + .events = events, + }; + int ret; + + ret = poll(&fds, 1, priv->polltimeout); if (ret == -EINTR) continue; - else - return ret; + else if (ret < 0) + return -errno; + else if (!ret) + return -ETIME; + + if (!(fds.revents & events)) { + warn("%s: something else is wrong", __func__); + return -EIO; + } + + if (fds.revents & POLLOUT) { + num_sent = jcat_send_one(priv, out_fd, tmp_buf, count); + if (num_sent < 0) + return num_sent; + } + } else { + num_sent = jcat_send_one(priv, out_fd, tmp_buf, count); + if (num_sent < 0) + return num_sent; } - num_sent = jcat_send_one(priv, out_fd, tmp_buf, count); - if (num_sent < 0) - return num_sent; count -= num_sent; tmp_buf += num_sent; From edcce70373e54cbdbea9a38cccce111630772fec Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Thu, 9 May 2019 09:26:21 +0200 Subject: [PATCH 6/7] jcat: provide errqueue support The J1939 errqueue is a feedback interface to notify userspace applications about actual transfer status. For now we can get information about amount of data already send to the peer and errors if session was aborted. Signed-off-by: Oleksij Rempel --- jcat.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 218 insertions(+), 3 deletions(-) diff --git a/jcat.c b/jcat.c index e813af6..0b8ea86 100644 --- a/jcat.c +++ b/jcat.c @@ -16,6 +16,11 @@ #include #include +#include +#include +#include +#include + #include "libj1939.h" #define J1939_MAX_ETP_PACKET_SIZE (7 * 0x00ffffff) #define JCAT_BUF_SIZE (1000 * 1024) @@ -31,6 +36,13 @@ (void) (&_min1 == &_min2); \ _min1 < _min2 ? _min1 : _min2; }) + +struct jcat_stats { + int err; + uint32_t tskey; + uint32_t send; +}; + struct jcat_priv { int sock; int infile; @@ -47,6 +59,10 @@ struct jcat_priv { struct sockaddr_can sockname; struct sockaddr_can peername; + + struct sock_extended_err *serr; + struct scm_timestamping *tss; + struct jcat_stats stats; }; static const char help_msg[] = @@ -111,16 +127,181 @@ static ssize_t jcat_send_one(struct jcat_priv *priv, int out_fd, return num_sent; } +static void jcat_print_timestamp(struct jcat_priv *priv, const char *name, + struct timespec *cur) +{ + struct jcat_stats *stats = &priv->stats; + + if (!(cur->tv_sec | cur->tv_nsec)) + return; + + fprintf(stderr, " %s: %lu s %lu us (seq=%u, send=%u)", + name, cur->tv_sec, cur->tv_nsec / 1000, + stats->tskey, stats->send); + + fprintf(stderr, "\n"); +} + +static const char *jcat_tstype_to_str(int tstype) +{ + switch (tstype) { + case SCM_TSTAMP_SCHED: + return " ENQ"; + case SCM_TSTAMP_SND: + return " SND"; + case SCM_TSTAMP_ACK: + return " ACK"; + default: + return " unk"; + } +} + +/* Check the stats of SCM_TIMESTAMPING_OPT_STATS */ +static void jcat_scm_opt_stats(struct jcat_priv *priv, void *buf, int len) +{ + struct jcat_stats *stats = &priv->stats; + int offset = 0; + + while (offset < len) { + struct nlattr *nla = (struct nlattr *) (buf + offset); + + switch (nla->nla_type) { + case J1939_NLA_BYTES_ACKED: + stats->send = *(uint32_t *)((void *)nla + NLA_HDRLEN); + break; + default: + warnx("not supported J1939_NLA field\n"); + } + + offset += NLA_ALIGN(nla->nla_len); + } +} + +static int jcat_extract_serr(struct jcat_priv *priv) +{ + struct jcat_stats *stats = &priv->stats; + struct sock_extended_err *serr = priv->serr; + struct scm_timestamping *tss = priv->tss; + + switch (serr->ee_origin) { + case SO_EE_ORIGIN_TIMESTAMPING: + /* + * We expect here following patterns: + * serr->ee_info == SCM_TSTAMP_ACK + * Activated with SOF_TIMESTAMPING_TX_ACK + * or + * serr->ee_info == SCM_TSTAMP_SCHED + * Activated with SOF_TIMESTAMPING_SCHED + * and + * serr->ee_data == tskey + * session message counter which is activate + * with SOF_TIMESTAMPING_OPT_ID + * the serr->ee_errno should be ENOMSG + */ + if (serr->ee_errno != ENOMSG) + warnx("serr: expected ENOMSG, got: %i", + serr->ee_errno); + stats->tskey = serr->ee_data; + + jcat_print_timestamp(priv, jcat_tstype_to_str(serr->ee_info), + &tss->ts[0]); + + if (serr->ee_info == SCM_TSTAMP_SCHED) + return -EINTR; + else + return 0; + case SO_EE_ORIGIN_LOCAL: + /* + * The serr->ee_origin == SO_EE_ORIGIN_LOCAL is + * currently used to notify about locally + * detected protocol/stack errors. + * Following patterns are expected: + * serr->ee_info == J1939_EE_INFO_TX_ABORT + * is used to notify about session TX + * abort. + * serr->ee_data == tskey + * session message counter which is activate + * with SOF_TIMESTAMPING_OPT_ID + * serr->ee_errno == actual error reason + * error reason is converted from J1939 + * abort to linux error name space. + */ + if (serr->ee_info != J1939_EE_INFO_TX_ABORT) + warnx("serr: unknown ee_info: %i", + serr->ee_info); + + jcat_print_timestamp(priv, " ABT", &tss->ts[0]); + warnx("serr: tx error: %i, %s", serr->ee_errno, strerror(serr->ee_errno)); + + return serr->ee_errno; + default: + warnx("serr: wrong origin: %u", serr->ee_origin); + } + + return 0; +} + +static int jcat_parse_cm(struct jcat_priv *priv, struct cmsghdr *cm) +{ + const size_t hdr_len = CMSG_ALIGN(sizeof(struct cmsghdr)); + + if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_TIMESTAMPING) { + priv->tss = (void *)CMSG_DATA(cm); + } else if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_TIMESTAMPING_OPT_STATS) { + void *jstats = (void *)CMSG_DATA(cm); + + /* Activated with SOF_TIMESTAMPING_OPT_STATS */ + jcat_scm_opt_stats(priv, jstats, cm->cmsg_len - hdr_len); + } else if (cm->cmsg_level == SOL_CAN_J1939 && + cm->cmsg_type == SCM_J1939_ERRQUEUE) { + priv->serr = (void *)CMSG_DATA(cm); + } else + warnx("serr: not supported type: %d.%d", + cm->cmsg_level, cm->cmsg_type); + + return 0; +} + +static int jcat_recv_err(struct jcat_priv *priv) +{ + char control[100]; + struct cmsghdr *cm; + int ret; + struct msghdr msg = { + .msg_control = control, + .msg_controllen = sizeof(control), + }; + + ret = recvmsg(priv->sock, &msg, MSG_ERRQUEUE); + if (ret == -1) + err(EXIT_FAILURE, "recvmsg error notification: %i", errno); + if (msg.msg_flags & MSG_CTRUNC) + err(EXIT_FAILURE, "recvmsg error notification: truncated"); + + priv->serr = NULL; + priv->tss = NULL; + + for (cm = CMSG_FIRSTHDR(&msg); cm && cm->cmsg_len; + cm = CMSG_NXTHDR(&msg, cm)) { + jcat_parse_cm(priv, cm); + if (priv->serr && priv->tss) + return jcat_extract_serr(priv); + } + + return 0; +} + static int jcat_send_loop(struct jcat_priv *priv, int out_fd, char *buf, size_t buf_size) { - ssize_t count, num_sent = 0; + ssize_t count, num_sent; char *tmp_buf = buf; - unsigned int events = POLLOUT; + unsigned int events = POLLOUT | POLLERR; + bool tx_done = false; count = buf_size; - while (count) { + while (!tx_done) { if (priv->polltimeout) { struct pollfd fds = { .fd = priv->sock, @@ -141,6 +322,17 @@ static int jcat_send_loop(struct jcat_priv *priv, int out_fd, char *buf, return -EIO; } + if (fds.revents & POLLERR) { + ret = jcat_recv_err(priv); + if (ret == -EINTR) + continue; + else if (ret) + return ret; + else + tx_done = true; + + } + if (fds.revents & POLLOUT) { num_sent = jcat_send_one(priv, out_fd, tmp_buf, count); if (num_sent < 0) @@ -159,6 +351,8 @@ static int jcat_send_loop(struct jcat_priv *priv, int out_fd, char *buf, __func__); return -EINVAL; } + if (!count) + events = POLLERR; } return 0; } @@ -306,6 +500,8 @@ static int jcat_recv(struct jcat_priv *priv) static int jcat_sock_prepare(struct jcat_priv *priv) { + unsigned int sock_opt; + int value; int ret; /* open socket */ @@ -324,6 +520,25 @@ static int jcat_sock_prepare(struct jcat_priv *priv) } } + value = 1; + ret = setsockopt(priv->sock, SOL_CAN_J1939, SO_J1939_ERRQUEUE, &value, + sizeof(value)); + if (ret < 0) { + warn("set recverr"); + return EXIT_FAILURE; + } + + sock_opt = SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_OPT_CMSG | + SOF_TIMESTAMPING_TX_ACK | + SOF_TIMESTAMPING_TX_SCHED | + SOF_TIMESTAMPING_OPT_STATS | SOF_TIMESTAMPING_OPT_TSONLY | + SOF_TIMESTAMPING_OPT_ID; + + if (setsockopt(priv->sock, SOL_SOCKET, SO_TIMESTAMPING, + (char *) &sock_opt, sizeof(sock_opt))) + error(1, 0, "setsockopt timestamping"); + ret = bind(priv->sock, (void *)&priv->sockname, sizeof(priv->sockname)); if (ret < 0) { warn("bind()"); From 25025d05ae4a97034f479dacaa68ee2cbd0349d1 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Thu, 2 May 2019 08:43:08 +0200 Subject: [PATCH 7/7] jcat: add repeat support Add option to resend same data without closing the socket. Signed-off-by: Oleksij Rempel --- jcat.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/jcat.c b/jcat.c index 0b8ea86..cbbc62a 100644 --- a/jcat.c +++ b/jcat.c @@ -48,6 +48,7 @@ struct jcat_priv { int infile; int outfile; size_t max_transfer; + int repeat; int todo_prio; bool valid_peername; @@ -75,6 +76,7 @@ static const char help_msg[] = " -r Receive data\n" " -P poll timeout in milliseconds before sending data.\n" " With this option send() will be used with MSG_DONTWAIT flag.\n" + " -R Set send repeat count. Default: 1\n" "\n" "Example:\n" "jcat -i some_file_to_send can0:0x80 :0x90,0x12300\n" @@ -82,7 +84,7 @@ static const char help_msg[] = "\n" ; -static const char optstring[] = "?i:vs:rp:P:cnw::"; +static const char optstring[] = "?i:vs:rp:P:R:"; static void jcat_init_sockaddr_can(struct sockaddr_can *sac) @@ -294,7 +296,7 @@ static int jcat_recv_err(struct jcat_priv *priv) static int jcat_send_loop(struct jcat_priv *priv, int out_fd, char *buf, size_t buf_size) { - ssize_t count, num_sent; + ssize_t count, num_sent = 0; char *tmp_buf = buf; unsigned int events = POLLOUT | POLLERR; bool tx_done = false; @@ -445,6 +447,7 @@ static size_t jcat_get_file_size(int fd) static int jcat_send(struct jcat_priv *priv) { unsigned int size = 0; + int ret, i; if (priv->todo_filesize) size = jcat_get_file_size(priv->infile); @@ -452,8 +455,16 @@ static int jcat_send(struct jcat_priv *priv) if (!size) return EXIT_FAILURE; - return jcat_sendfile(priv, priv->sock, priv->infile, NULL, - size); + for (i = 0; i < priv->repeat; i++) { + ret = jcat_sendfile(priv, priv->sock, priv->infile, NULL, size); + if (ret) + break; + + if (lseek(priv->infile, 0, SEEK_SET) == -1) + error(1, errno, "%s lseek() start\n", __func__); + } + + return ret; } static int jcat_recv_one(struct jcat_priv *priv, uint8_t *buf, size_t buf_size) @@ -593,6 +604,11 @@ static int jcat_parse_args(struct jcat_priv *priv, int argc, char *argv[]) case 'c': priv->todo_connect = 1; break; + case 'R': + priv->repeat = atoi(optarg); + if (priv->repeat < 1) + err(EXIT_FAILURE, "send/repeat count can't be less then 1\n"); + break; default: fputs(help_msg, stderr); return EXIT_FAILURE; @@ -631,6 +647,7 @@ int main(int argc, char *argv[]) priv->outfile = STDOUT_FILENO; priv->max_transfer = J1939_MAX_ETP_PACKET_SIZE; priv->polltimeout = 100000; + priv->repeat = 1; jcat_init_sockaddr_can(&priv->sockname); jcat_init_sockaddr_can(&priv->peername);