diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index a40e41d80c..c992c3d375 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -43,6 +43,7 @@ PSEUDOMODULES += gnrc_netapi_callbacks PSEUDOMODULES += gnrc_netapi_mbox PSEUDOMODULES += gnrc_netif_bus PSEUDOMODULES += gnrc_netif_events +PSEUDOMODULES += gnrc_netif_timestamp PSEUDOMODULES += gnrc_pktbuf_cmd PSEUDOMODULES += gnrc_netif_6lo PSEUDOMODULES += gnrc_netif_ipv6 diff --git a/sys/Makefile.dep b/sys/Makefile.dep index c2f557cb6b..78d70c00dc 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -179,6 +179,9 @@ endif ifneq (,$(filter gnrc_sock_%,$(USEMODULE))) USEMODULE += gnrc_sock + ifneq (,$(filter sock_aux_timestamp,$(USEMODULE))) + USEMODULE += gnrc_netif_timestamp + endif endif ifneq (,$(filter gnrc_sock_async,$(USEMODULE))) diff --git a/sys/include/net/gnrc/netif/hdr.h b/sys/include/net/gnrc/netif/hdr.h index 3652565ee9..bc8e32ac5d 100644 --- a/sys/include/net/gnrc/netif/hdr.h +++ b/sys/include/net/gnrc/netif/hdr.h @@ -88,6 +88,17 @@ extern "C" { * @ref IEEE802154_FCF_FRAME_PEND */ #define GNRC_NETIF_HDR_FLAGS_MORE_DATA (0x10) + +/** + * @brief Indicate presence of a valid timestamp + * + * @details If (and only if) module `gnrc_netif_timestamp` is used and the + * network device supplied the timestamp of reception of a frame, this + * timestamp is passed up the network stack through + * @ref gnrc_netif_hdr_t::timestamp and this flag is set. This flag + * can be used to check for presence of a valid timestamp. + */ +#define GNRC_NETIF_HDR_FLAGS_TIMESTAMP (0x08) /** * @} */ @@ -105,6 +116,24 @@ typedef struct { uint8_t flags; /**< flags as defined above */ uint8_t lqi; /**< lqi of received packet (optional) */ int16_t rssi; /**< rssi of received packet in dBm (optional) */ +#if IS_USED(MODULE_GNRC_NETIF_TIMESTAMP) || defined(DOXYGEN) + /** + * @brief Timestamp of reception in nanoseconds since epoch + * + * @note Only when @ref GNRC_NETIF_HDR_FLAGS_TIMESTAMP is set, this + * field contains valid info. + * + * This field is only provided if module `gnrc_netif_timestamp` is used. + * Keep in mind that when the hardware does not provide timestamping, the + * network device driver could choose to provide this in software, which + * adds the delay and jitter of the ISR handling to the timestamp. Please + * keep also in mind that a hardware implementation might not be able to + * reliable timestamp every frame - e.g. a full-duplex wired interface might + * be unable to timestamp a received frame while timestamping an outgoing + * frame. + */ + uint64_t timestamp; +#endif /* MODULE_GNRC_NETIF_TIMESTAMP */ } gnrc_netif_hdr_t; /** @@ -201,6 +230,50 @@ static inline void gnrc_netif_hdr_set_dst_addr(gnrc_netif_hdr_t *hdr, memcpy(((uint8_t *)(hdr + 1)) + hdr->src_l2addr_len, addr, addr_len); } +/** + * @brief Set the timestamp in the netif header + * @param[out] hdr Header to set the timestamp in + * @param[in] timestamp Timestamp to set (nanoseconds since epoch) + * + * @details If the module gnrc_netif_timestamp is not used, a call to this + * function become a non-op (and will be fully optimized out by the + * compiler) + */ +static inline void gnrc_netif_hdr_set_timestamp(gnrc_netif_hdr_t *hdr, + uint64_t timestamp) +{ + (void)hdr; + (void)timestamp; +#if IS_USED(MODULE_GNRC_NETIF_TIMESTAMP) + hdr->timestamp = timestamp; + hdr->flags |= GNRC_NETIF_HDR_FLAGS_TIMESTAMP; +#endif +} + +/** + * @brief Get the timestamp of the frame in nanoseconds since epoch + * @param[in] hdr Header to read the timestamp from + * @param[out] dest The timestamp will be stored here + * @retval 0 The timestamp was stored in @p dest + * @retval -1 No timestamp available, @p dest is unchanged + * + * @details If the module gnrc_netif_timestamp is not used, this will always + * return 0 + */ +static inline int gnrc_netif_hdr_get_timestamp(const gnrc_netif_hdr_t *hdr, + uint64_t *dest) +{ + (void)hdr; + (void)dest; +#if IS_USED(MODULE_GNRC_NETIF_TIMESTAMP) + if (hdr->flags & GNRC_NETIF_HDR_FLAGS_TIMESTAMP) { + *dest = hdr->timestamp; + return 0; + } +#endif + return -1; +} + #if defined(MODULE_GNRC_IPV6) || defined(DOXYGEN) /** * @brief Converts the source address of a given @ref net_gnrc_netif_hdr to diff --git a/sys/net/gnrc/sock/gnrc_sock.c b/sys/net/gnrc/sock/gnrc_sock.c index 1b7e15886e..aabee4c349 100644 --- a/sys/net/gnrc/sock/gnrc_sock.c +++ b/sys/net/gnrc/sock/gnrc_sock.c @@ -90,7 +90,7 @@ void gnrc_sock_create(gnrc_sock_reg_t *reg, gnrc_nettype_t type, uint32_t demux_ ssize_t gnrc_sock_recv(gnrc_sock_reg_t *reg, gnrc_pktsnip_t **pkt_out, uint32_t timeout, sock_ip_ep_t *remote, - gnrc_sock_recv_aux_t aux) + gnrc_sock_recv_aux_t *aux) { /* only used when some sock_aux_% module is used */ (void)aux; @@ -154,9 +154,9 @@ ssize_t gnrc_sock_recv(gnrc_sock_reg_t *reg, gnrc_pktsnip_t **pkt_out, memcpy(&remote->addr, &ipv6_hdr->src, sizeof(ipv6_addr_t)); remote->family = AF_INET6; #if IS_USED(MODULE_SOCK_AUX_LOCAL) - if (aux.local != NULL) { - memcpy(&aux.local->addr, &ipv6_hdr->dst, sizeof(ipv6_addr_t)); - aux.local->family = AF_INET6; + if (aux->local != NULL) { + memcpy(&aux->local->addr, &ipv6_hdr->dst, sizeof(ipv6_addr_t)); + aux->local->family = AF_INET6; } #endif /* MODULE_SOCK_AUX_LOCAL */ netif = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF); @@ -167,6 +167,13 @@ ssize_t gnrc_sock_recv(gnrc_sock_reg_t *reg, gnrc_pktsnip_t **pkt_out, gnrc_netif_hdr_t *netif_hdr = netif->data; /* TODO: use API in #5511 */ remote->netif = (uint16_t)netif_hdr->if_pid; +#if IS_USED(MODULE_SOCK_AUX_TIMESTAMP) + if (aux->timestamp != NULL) { + if (gnrc_netif_hdr_get_timestamp(netif_hdr, aux->timestamp) == 0) { + aux->flags |= GNRC_SOCK_RECV_AUX_FLAG_TIMESTAMP; + } + } +#endif /* MODULE_SOCK_AUX_TIMESTAMP */ } *pkt_out = pkt; /* set out parameter */ diff --git a/sys/net/gnrc/sock/include/gnrc_sock_internal.h b/sys/net/gnrc/sock/include/gnrc_sock_internal.h index df38e233de..485278f742 100644 --- a/sys/net/gnrc/sock/include/gnrc_sock_internal.h +++ b/sys/net/gnrc/sock/include/gnrc_sock_internal.h @@ -70,18 +70,17 @@ typedef struct { */ sock_ip_ep_t *local; #endif -#if !IS_USED(MODULE_SOCK_AUX_LOCAL) || DOXYGEN - /** - * @brief Workaround in case no `sock_aux_%` module is used - * - * Empty structures are only allowed with a GNU extension. For portability, - * this member is present if and only if this structure would be otherwise - * empty. - */ - uint8_t this_struct_is_not_empty; +#if IS_USED(MODULE_SOCK_AUX_TIMESTAMP) || DOXYGEN + uint64_t *timestamp; /**< timestamp PDU was received at in nanoseconds */ #endif + /** + * @brief Flags + */ + uint8_t flags; } gnrc_sock_recv_aux_t; +#define GNRC_SOCK_RECV_AUX_FLAG_TIMESTAMP 0x01 /**< Timestamp valid */ + /** * @brief Internal helper functions for GNRC * @internal @@ -143,7 +142,7 @@ void gnrc_sock_create(gnrc_sock_reg_t *reg, gnrc_nettype_t type, uint32_t demux_ * @internal */ ssize_t gnrc_sock_recv(gnrc_sock_reg_t *reg, gnrc_pktsnip_t **pkt, uint32_t timeout, - sock_ip_ep_t *remote, gnrc_sock_recv_aux_t aux); + sock_ip_ep_t *remote, gnrc_sock_recv_aux_t *aux); /** * @brief Send a packet internally diff --git a/sys/net/gnrc/sock/ip/gnrc_sock_ip.c b/sys/net/gnrc/sock/ip/gnrc_sock_ip.c index e10a19021b..86521f45c6 100644 --- a/sys/net/gnrc/sock/ip/gnrc_sock_ip.c +++ b/sys/net/gnrc/sock/ip/gnrc_sock_ip.c @@ -135,7 +135,12 @@ ssize_t sock_ip_recv_buf_aux(sock_ip_t *sock, void **data, void **buf_ctx, _aux.local = &aux->local; } #endif - res = gnrc_sock_recv((gnrc_sock_reg_t *)sock, &pkt, timeout, &tmp, _aux); +#if IS_USED(MODULE_SOCK_AUX_TIMESTAMP) + if ((aux != NULL) && (aux->flags & SOCK_AUX_GET_TIMESTAMP)) { + _aux.timestamp = &aux->timestamp; + } +#endif + res = gnrc_sock_recv((gnrc_sock_reg_t *)sock, &pkt, timeout, &tmp, &_aux); if (res < 0) { return res; } @@ -156,6 +161,16 @@ ssize_t sock_ip_recv_buf_aux(sock_ip_t *sock, void **data, void **buf_ctx, if ((aux != NULL) && (aux->flags & SOCK_AUX_GET_LOCAL)) { aux->flags &= ~(SOCK_AUX_GET_LOCAL); } +#endif +#if IS_USED(MODULE_SOCK_AUX_TIMESTAMP) + if ((aux != NULL) && (aux->flags & SOCK_AUX_GET_TIMESTAMP)) { + /* check if network interface did provide a timestamp; this depends on + * hardware support. A timestamp of zero is used to indicate a missing + * timestamp */ + if (aux->timestamp > 0) { + aux->flags &= ~SOCK_AUX_GET_TIMESTAMP; + } + } #endif *data = pkt->data; *buf_ctx = pkt; diff --git a/sys/net/gnrc/sock/udp/gnrc_sock_udp.c b/sys/net/gnrc/sock/udp/gnrc_sock_udp.c index fd81ec83d2..dbfa9cbcc5 100644 --- a/sys/net/gnrc/sock/udp/gnrc_sock_udp.c +++ b/sys/net/gnrc/sock/udp/gnrc_sock_udp.c @@ -224,7 +224,12 @@ ssize_t sock_udp_recv_buf_aux(sock_udp_t *sock, void **data, void **buf_ctx, _aux.local = (sock_ip_ep_t *)&aux->local; } #endif - res = gnrc_sock_recv((gnrc_sock_reg_t *)sock, &pkt, timeout, &tmp, _aux); +#if IS_USED(MODULE_SOCK_AUX_TIMESTAMP) + if ((aux != NULL) && (aux->flags & SOCK_AUX_GET_TIMESTAMP)) { + _aux.timestamp = &aux->timestamp; + } +#endif + res = gnrc_sock_recv((gnrc_sock_reg_t *)sock, &pkt, timeout, &tmp, &_aux); if (res < 0) { return res; } @@ -251,6 +256,16 @@ ssize_t sock_udp_recv_buf_aux(sock_udp_t *sock, void **data, void **buf_ctx, aux->flags &= ~SOCK_AUX_GET_LOCAL; aux->local.port = sock->local.port; } +#endif +#if IS_USED(MODULE_SOCK_AUX_TIMESTAMP) + if ((aux != NULL) && (aux->flags & SOCK_AUX_GET_TIMESTAMP)) { + /* check if network interface did provide a timestamp; this depends on + * hardware support. A timestamp of zero is used to indicate a missing + * timestamp */ + if (_aux.flags & GNRC_SOCK_RECV_AUX_FLAG_TIMESTAMP) { + aux->flags &= ~SOCK_AUX_GET_TIMESTAMP; + } + } #endif *data = pkt->data; *buf_ctx = pkt;