flexPTP 1.0
An IEEE 1588 PTP implementation designed for microcontrollers
Loading...
Searching...
No Matches
nsd_lwip.c
Go to the documentation of this file.
1#include "../../network_stack_driver.h"
2
3#include "../../ptp_defs.h"
4#include "../../task_ptp.h"
5
6#include "lwip/err.h"
7#include "lwip/igmp.h"
8#include "lwip/ip4_addr.h"
9#include "lwip/ip_addr.h"
10#include "lwip/netif.h"
11#include "lwip/udp.h"
12#include "netif/ethernet.h"
13
14#include <string.h>
15
16// initialize connection blocks to invalid states
17static struct udp_pcb *PTP_L4_PRIMARY_EVENT = NULL;
18static struct udp_pcb *PTP_L4_PRIMARY_GENERAL = NULL;
19static struct udp_pcb *PTP_L4_PDELAY_EVENT = NULL;
20static struct udp_pcb *PTP_L4_PDELAY_GENERAL = NULL;
21
22// store current settings
23static PtpTransportType TP = -1;
26static uint8_t custom_p2p_8023_primary_dest[6] = {};
28static uint8_t custom_p2p_8023_pdel_dest[6] = {};
29
30static const uint8_t zero_mac[6] = {};
31
32static void ptp_transmit_cb(uint32_t ts_s, uint32_t ts_ns, void *tag);
33static void ptp_receive_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
34
35void ptp_nsd_igmp_join_leave(bool join) {
36 // only join IGMP if Transport Type is IP
37 if (TP == PTP_TP_IPv4) {
38 err_t (*igmp_fn)(const ip_addr_t *, const ip_addr_t *) = join ? igmp_joingroup : igmp_leavegroup; // join or leave
39
40 igmp_fn(&netif_default->ip_addr, &PTP_IGMP_PRIMARY); // join E2E DM message group
41
42 if (DM == PTP_DM_P2P) {
43 igmp_fn(&netif_default->ip_addr, &PTP_IGMP_PEER_DELAY); // join P2P DM message group
44 }
45 }
46}
47
48void ptp_nsd_init(const NsdInitSettings * init) {
49 // lock LWIP core
50 LOCK_TCPIP_CORE();
51
52 // leave current IGMP group if applicable
54
55 // first, close all open connection blocks (zero CBDs won't cause trouble)
56 if (PTP_L4_PRIMARY_EVENT != NULL) {
57 udp_disconnect(PTP_L4_PRIMARY_EVENT);
58 udp_remove(PTP_L4_PRIMARY_EVENT);
60 }
61 if (PTP_L4_PRIMARY_GENERAL != NULL) {
62 udp_disconnect(PTP_L4_PRIMARY_GENERAL);
63 udp_remove(PTP_L4_PRIMARY_GENERAL);
65 }
66 if (PTP_L4_PDELAY_EVENT != NULL) {
67 udp_disconnect(PTP_L4_PDELAY_EVENT);
68 udp_remove(PTP_L4_PDELAY_EVENT);
70 }
71 if (PTP_L4_PDELAY_GENERAL != NULL) {
72 udp_disconnect(PTP_L4_PDELAY_GENERAL);
73 udp_remove(PTP_L4_PDELAY_GENERAL);
75 }
76
77 // calling either parameter with -1 just closes connections
78 if ((init->tp == -1) || (init->dm == -1)) {
79 // message transmission and reception is turned off
80 TP = -1;
81 DM = -1;
82 return;
83 }
84
85 // open only the necessary ones
86 if (init->tp == PTP_TP_IPv4) {
87 // open event and general PRIMARY connections
88 PTP_L4_PRIMARY_EVENT = udp_new();
90 udp_recv(PTP_L4_PRIMARY_EVENT, ptp_receive_cb, NULL);
91
92 PTP_L4_PRIMARY_GENERAL = udp_new();
95
96 // open event and general PDELAY* connections
97 if (init->dm == PTP_DM_P2P) {
98 PTP_L4_PDELAY_EVENT = udp_new();
100 udp_recv(PTP_L4_PDELAY_EVENT, ptp_receive_cb, NULL);
101
102 PTP_L4_PDELAY_GENERAL = udp_new();
104 udp_recv(PTP_L4_PDELAY_GENERAL, ptp_receive_cb, NULL);
105 }
106 }
107
108 /* Custom P2P 802.3 destinations, RECOMPUTED rather than latched.
109 *
110 * An all-zero field means "use the standard address", and it is expressed by the absence
111 * of an override -- so a profile that supplies none has to take the previous profile's
112 * override out of effect. ptp_nsd_init() runs on every profile change, and flags that are
113 * only ever set to true made the switch one-way: gPTP, which supplies 01:80:C2:00:00:0E,
114 * followed by any other preset, left the node still transmitting to 01:80:C2:00:00:0E
115 * while reporting the new profile. */
116 const uint8_t mac_size = sizeof(zero_mac);
117
119 (memcmp(init->primary_p2p_8023_dest, zero_mac, mac_size) != 0);
122 }
123
125 (memcmp(init->pdelay_p2p_8023_dest, zero_mac, mac_size) != 0);
127 memcpy(custom_p2p_8023_pdel_dest, init->pdelay_p2p_8023_dest, mac_size);
128 }
129
130 // store configuration
131 TP = init->tp;
132 DM = init->dm;
133
134 // join new IGMP group
136
137 // unlock LWIP core
138 UNLOCK_TCPIP_CORE();
139}
140
141static void ptp_receive_cb(void *pArg, struct udp_pcb *pPCB, struct pbuf *pP, const ip_addr_t *pAddr, uint16_t port) {
142 // put msg into the queue
143 ptp_receive_enqueue(pP->payload, pP->len, pP->time_s, pP->time_ns, PTP_TP_IPv4);
144
145 // release pbuf resources
146 pbuf_free(pP);
147}
148
149static void ptp_transmit_cb(uint32_t ts_s, uint32_t ts_ns, void *tag) {
150 ptp_transmit_timestamp_cb((uint32_t)tag, ts_s, ts_ns);
151}
152
153void ptp_nsd_transmit_msg(RawPtpMessage *pMsg, uint32_t uid) {
154 if (pMsg == NULL) {
155 MSG("NULL!!!\n");
156 return;
157 }
158
159 PtpMessageClass mc = pMsg->tx_mc;
160 PtpMessageType mt = pMsg->tx_mt;
161
162 // allocate buffer
163 struct pbuf *p = NULL;
164 p = pbuf_alloc((TP == PTP_TP_IPv4) ? PBUF_TRANSPORT : PBUF_LINK, pMsg->size, PBUF_RAM);
165
166 /* pbuf_alloc() returns NULL when lwIP's heap is exhausted, which sustained receive pressure
167 * will do. Dereferencing it here faulted the board rather than dropping a message -- and a
168 * transmit that cannot get memory is exactly the case where dropping is right. */
169 if (p == NULL) {
170 return;
171 }
172
173 // fill buffer
174 memcpy(p->payload, pMsg->data, pMsg->size);
175
176 // set transmit callback
177 p->tag = (void *)uid;
178 p->tx_cb = ptp_transmit_cb;
179
180 // lock LWIP core
181 LOCK_TCPIP_CORE();
182
183 // is it a Peer Delay Mechanism related message?
184 bool isPDel_ = (mt == PTP_MT_PDelay_Req) || (mt == PTP_MT_PDelay_Resp) || (mt == PTP_MT_PDelay_Resp_Follow_Up);
185
186 // narrow down by transport type
187 if (TP == PTP_TP_IPv4) {
188 struct udp_pcb *conn = (mc == PTP_MC_EVENT) ? PTP_L4_PRIMARY_EVENT : PTP_L4_PRIMARY_GENERAL; // select connection by message type
189 uint16_t port = (mc == PTP_MC_EVENT) ? PTP_PORT_EVENT : PTP_PORT_GENERAL; // select port by message class
190 ip_addr_t ipaddr = isPDel_ ? PTP_IGMP_PEER_DELAY : PTP_IGMP_PRIMARY; // select destination IP-address by PDel*/primary message types
191 udp_sendto(conn, p, &ipaddr, port); // send packet
192 } else if (TP == PTP_TP_802_3) {
193 const uint8_t *ethaddr = isPDel_ ?
195 (custom_p2p_8023_primary_dest_valid ? custom_p2p_8023_primary_dest : PTP_ETHERNET_PRIMARY); // select destination address by PDel*/primary message types
196 ethernet_output(netif_default, p, (struct eth_addr *)netif_default->hwaddr, (struct eth_addr *)ethaddr, ETHERTYPE_PTP);
197 }
198
199 // unlock LWIP core
200 UNLOCK_TCPIP_CORE();
201
202 /* Untag the pbuf before releasing it.
203 *
204 * tx_cb and tag live in LWIP_PBUF_CUSTOM_DATA, which lwIP does not initialise on
205 * allocation -- it manages its own fields and nothing else. So whatever is left here
206 * survives into the next allocation that lands on this memory, and a port that gates
207 * hardware transmit timestamping on `p->tx_cb != NULL`, which is the only signal this
208 * driver offers, then requests a timestamp for a frame that never asked for one and
209 * reports it against a stale uid.
210 *
211 * The send above is synchronous -- udp_sendto() and ethernet_output() reach
212 * netif->linkoutput before returning -- so the driver has already taken both values. */
213 p->tx_cb = NULL;
214 p->tag = NULL;
215
216 pbuf_free(p); // release buffer
217}
218
219void ptp_transmit_free(struct pbuf *pPBuf) {
220 pbuf_free(pPBuf);
221}
222
224 memcpy(hwa, netif_default->hwaddr, netif_default->hwaddr_len);
225}
226
227#define ETHERNET_HEADER_LENGTH (14)
228
229// hook for L2 PTP messages
230err_t hook_unknown_ethertype(struct pbuf *pbuf, struct netif *netif) {
231 // aquire ethertype
232 uint16_t etherType = 0;
233 memcpy(&etherType, ((uint8_t *)pbuf->payload) + 12, 2);
234 etherType = FLEXPTP_ntohs(etherType);
235 if (etherType == ETHERTYPE_PTP) {
236 // verify Ethernet address
237 if (!memcmp(PTP_ETHERNET_PRIMARY, pbuf->payload, 6) || !memcmp(PTP_ETHERNET_PEER_DELAY, pbuf->payload, 6)) { //
238 ptp_receive_enqueue(((uint8_t *)pbuf->payload) + ETHERNET_HEADER_LENGTH, pbuf->len - ETHERNET_HEADER_LENGTH, pbuf->time_s, pbuf->time_ns, PTP_TP_802_3);
239 }
240 }
241
242 pbuf_free(pbuf);
243
244 return ERR_OK;
245}
void ptp_transmit_free(struct pbuf *pPBuf)
Definition: nsd_lwip.c:219
static struct udp_pcb * PTP_L4_PRIMARY_GENERAL
Definition: nsd_lwip.c:18
void ptp_nsd_transmit_msg(RawPtpMessage *pMsg, uint32_t uid)
Definition: nsd_lwip.c:153
err_t hook_unknown_ethertype(struct pbuf *pbuf, struct netif *netif)
Definition: nsd_lwip.c:230
void ptp_nsd_init(const NsdInitSettings *init)
Definition: nsd_lwip.c:48
static PtpDelayMechanism DM
Definition: nsd_lwip.c:24
void ptp_nsd_igmp_join_leave(bool join)
Definition: nsd_lwip.c:35
static bool custom_p2p_8023_primary_dest_valid
Definition: nsd_lwip.c:25
static struct udp_pcb * PTP_L4_PDELAY_EVENT
Definition: nsd_lwip.c:19
#define ETHERNET_HEADER_LENGTH
Definition: nsd_lwip.c:227
static void ptp_receive_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
void ptp_nsd_get_interface_address(uint8_t *hwa)
Definition: nsd_lwip.c:223
static uint8_t custom_p2p_8023_primary_dest[6]
Definition: nsd_lwip.c:26
static bool custom_p2p_8023_pdel_dest_valid
Definition: nsd_lwip.c:27
static void ptp_transmit_cb(uint32_t ts_s, uint32_t ts_ns, void *tag)
Definition: nsd_lwip.c:149
static PtpTransportType TP
Definition: nsd_lwip.c:23
static const uint8_t zero_mac[6]
Definition: nsd_lwip.c:30
static uint8_t custom_p2p_8023_pdel_dest[6]
Definition: nsd_lwip.c:28
static struct udp_pcb * PTP_L4_PDELAY_GENERAL
Definition: nsd_lwip.c:20
static struct udp_pcb * PTP_L4_PRIMARY_EVENT
Definition: nsd_lwip.c:17
const uint8_t PTP_ETHERNET_PEER_DELAY[6]
PTP's L2 Peer_Delay Ethernet address.
Definition: ptp_defs.c:4
const uint8_t PTP_ETHERNET_PRIMARY[6]
PTP's L2 Primary Ethernet address.
Definition: ptp_defs.c:3
const ip_addr_t PTP_IGMP_PRIMARY
Primary IGMP address.
#define ETHERTYPE_PTP
(for lwIP conformity)
Definition: ptp_defs.h:38
#define PTP_PORT_EVENT
PTP event message port.
Definition: ptp_defs.h:45
#define FLEXPTP_ntohs(a)
Definition: ptp_defs.h:234
#define PTP_PORT_GENERAL
PTP general message port.
Definition: ptp_defs.h:46
ip4_addr ip_addr_t
Definition: ptp_defs.h:30
const ip_addr_t PTP_IGMP_PEER_DELAY
Peer_Delay IGMP address.
PtpTransportType
PTP transport type enumeration.
Definition: ptp_types.h:136
@ PTP_TP_IPv4
IPv4 Transport Type.
Definition: ptp_types.h:137
@ PTP_TP_802_3
Ethernet Transport Type.
Definition: ptp_types.h:138
PtpMessageClass
Enumeration for different PTP message classes.
Definition: ptp_types.h:160
@ PTP_MC_EVENT
Event Message Class.
Definition: ptp_types.h:161
PtpMessageType
PTP packet type enumeration.
Definition: ptp_types.h:37
@ PTP_MT_PDelay_Resp
Peer Delay Response.
Definition: ptp_types.h:41
@ PTP_MT_PDelay_Req
Peer Delay Request.
Definition: ptp_types.h:40
@ PTP_MT_PDelay_Resp_Follow_Up
Peer Delay Response Follow Up.
Definition: ptp_types.h:44
PtpDelayMechanism
PTP Delay mechanism enumeration.
Definition: ptp_types.h:144
@ PTP_DM_P2P
Peer-to-Peer Delay Mechanism.
Definition: ptp_types.h:146
Network Stack Driver initialization object.
uint8_t primary_p2p_8023_dest[6]
Destination address for primary P2P messages over Ethernet.
uint8_t pdelay_p2p_8023_dest[6]
Destination address for PDel* P2P messages over Ethernet.
PtpDelayMechanism dm
Delay Mechanism.
PtpTransportType tp
Transport Type.
PtpMessageClass tx_mc
transmit message class
Definition: ptp_types.h:195
PtpMessageType tx_mt
transmit message type
Definition: ptp_types.h:196
uint32_t size
Packet size.
Definition: ptp_types.h:188
uint8_t data[(128)]
raw packet data
Definition: ptp_types.h:199
void ptp_receive_enqueue(const void *pPayload, uint32_t len, uint32_t ts_sec, uint32_t ts_ns, int tp)
Definition: task_ptp.c:474
void ptp_transmit_timestamp_cb(uint32_t uid, uint32_t seconds, uint32_t nanoseconds)
Definition: task_ptp.c:604