1 /* PTP calibrate test.  This test case validates calibration of PTP client with different values. */
2 
3 #include   "netx_ptp_utility.h"
4 
5 #define     DEMO_STACK_SIZE    2048
6 
7 /* Define the ThreadX and NetX object control blocks...  */
8 
9 static TX_THREAD               ntest_0;
10 static TX_THREAD               ntest_1;
11 
12 static NX_PACKET_POOL          pool_0;
13 static NX_IP                   ip_0;
14 static NX_IP                   ip_1;
15 static NX_UDP_SOCKET           generic_socket;
16 static NX_UDP_SOCKET           event_socket;
17 static NX_PTP_CLIENT           ptp_client;
18 
19 static NX_PTP_TIME             sync_ts = {0x0, 0x5F97CD71, 0x240d93b2};
20 static NX_PTP_TIME             follow_up_ts = {0x0, 0x5F97CD71, 0x240e29e0};
21 static NX_PTP_TIME             delay_response_ts = {0x0, 0x5F97CD71, 0x2494b730};
22 static NX_PTP_TIME             synced_time;
23 static NX_PTP_TIME             expected_time;
24 static USHORT                  synced_utc_offset;
25 static USHORT                  expected_utc_offset;
26 static UCHAR                   ptp_stack[2048];
27 static UINT                    test_index;
28 
29 TEST_TIMESTAMP test_timestamps[] =
30 {
31 
32     /* Basic */
33     {
34         {0x0, 0x0, 0x0},                /* Sync */
35         {0x0, 0x5F97CD71, 0x240e29e0},  /* FollowUp */
36         {0x0, 0x5F97CD71, 0x2494b730},  /* DelayResponse */
37         {0x0, 0x0, 0x0},                /* FollowUp_received */
38         {0x0, 0x0, 0x0},                /* DelayRequest */
39     },
40 
41     /* offsetFromMaster is 0 */
42     {
43         {0x0, 0x0, 0x0},                /* Sync */
44         {0x0, 0x5F97CD71, 0x240e29e0},  /* FollowUp */
45         {0x0, 0x5F97CD71, 0x2494b730},  /* DelayResponse */
46         {0x0, 0x5F97CD71, 0x240e29e0},  /* FollowUp_received */
47         {0x0, 0x5F97CD71, 0x2494b730},  /* DelayRequest */
48     },
49 
50     /* offsetFromMaster is positive and less than 1s */
51     {
52         {0x0, 0x0, 0x0},                /* Sync */
53         {0x0, 0x5F97CD71, 0x240e29e0},  /* FollowUp */
54         {0x0, 0x5F97CD71, 0x2494b730},  /* DelayResponse */
55         {0x0, 0x5F97CD71, 0x240e29e0},  /* FollowUp_received */
56         {0x0, 0x5F97CD71, 0x2494b710},  /* DelayRequest */
57     },
58 
59     /* offsetFromMaster is positive and larger than 1s but need to increase second */
60     {
61         {0x0, 0x0, 0x0},                /* Sync */
62         {0x0, 0x5F97CD71, 0x240e29e0},  /* FollowUp */
63         {0x0, 0x5F97CD71, 0x2494b730},  /* DelayResponse */
64         {0x0, 0x5F97CD6F, 0x35efcce0},  /* FollowUp_received */
65         {0x0, 0x5F97CD6F, 0x36765a10},  /* DelayRequest */
66     },
67 
68     /* offsetFromMaster is positive and less than 1s but the nanosecond will overflow */
69     {
70         {0x0, 0x0, 0x0},                /* Sync */
71         {0x0, 0x5F97CD71, 0x240e29e0},  /* FollowUp */
72         {0x0, 0x5F97CD71, 0x2494b730},  /* DelayResponse */
73         {0x0, 0x5F97CD70, 0x35efcce0},  /* FollowUp_received */
74         {0x0, 0x5F97CD70, 0x36765a10},  /* DelayRequest */
75     },
76 
77     /* offsetFromMaster is negative and less than 1s */
78     {
79         {0x0, 0x0, 0x0},                /* Sync */
80         {0x0, 0x5F97CD71, 0x240e29e0},  /* FollowUp */
81         {0x0, 0x5F97CD71, 0x2494b730},  /* DelayResponse */
82         {0x0, 0x5F97CD71, 0x240e29f0},  /* FollowUp_received */
83         {0x0, 0x5F97CD71, 0x2494b740},  /* DelayRequest */
84     },
85 
86     /* offsetFromMaster is negative and larger than 1s but need to decrease second */
87     {
88         {0x0, 0x0, 0x0},                /* Sync */
89         {0x0, 0x5F97CD71, 0x240e29e0},  /* FollowUp */
90         {0x0, 0x5F97CD71, 0x2494b730},  /* DelayResponse */
91         {0x0, 0x5F97CD73, 0x3b4d1760},  /* FollowUp_received */
92         {0x0, 0x5F97CD74, 0x00000040},  /* DelayRequest */
93     },
94 
95     /* offsetFromMaster is negative and less than 1s but need to decrease second */
96     {
97         {0x0, 0x0, 0x0},                /* Sync */
98         {0x0, 0x5F97CD71, 0x240e29e0},  /* FollowUp */
99         {0x0, 0x5F97CD71, 0x2494b730},  /* DelayResponse */
100         {0x0, 0x5F97CD71, 0x3b4d1760},  /* FollowUp_received */
101         {0x0, 0x5F97CD72, 0x00000040},  /* DelayRequest */
102     },
103 };
104 
105 
106 #define NUM_PACKETS            24
107 #define PACKET_SIZE            1536
108 #define PACKET_POOL_SIZE       (NUM_PACKETS * (PACKET_SIZE + sizeof(NX_PACKET)))
109 
110 /* Define thread prototypes.  */
111 
112 static void    ntest_0_entry(ULONG thread_input);
113 static void    ntest_1_entry(ULONG thread_input);
114 extern void    _nx_ram_network_driver(struct NX_IP_DRIVER_STRUCT *driver_req);
115 
116 
117 /* Define what the initial system looks like.  */
118 #ifdef CTEST
test_application_define(void * first_unused_memory)119 VOID test_application_define(void *first_unused_memory)
120 #else
121 void netx_ptp_client_calibrate_application_define(void *first_unused_memory)
122 #endif
123 {
124 CHAR       *pointer;
125 UINT       status;
126 
127     /* Print out test information banner.  */
128     printf("NetX Test:   PTP Calibrate Test .......................................");
129 
130 #if defined(NX_ENABLE_GPTP) || (NX_PTP_CLIENT_TRANSPORT_UDP==0)
131     printf("N/A\n");
132     test_control_return(3);
133 #else
134     /* Setup the working pointer.  */
135     pointer = (CHAR *) first_unused_memory;
136 
137     /* Create the main thread.  */
138     tx_thread_create(&ntest_0, "thread 0", ntest_0_entry, 0,
139                      pointer, DEMO_STACK_SIZE,
140                      4, 4, TX_NO_TIME_SLICE, TX_AUTO_START);
141 
142     pointer = pointer + DEMO_STACK_SIZE;
143 
144     /* Create the main thread.  */
145     tx_thread_create(&ntest_1, "thread 1", ntest_1_entry, 0,
146                      pointer, DEMO_STACK_SIZE,
147                      3, 3, TX_NO_TIME_SLICE, TX_AUTO_START);
148 
149     pointer = pointer + DEMO_STACK_SIZE;
150 
151     /* Initialize the NetX system.  */
152     nx_system_initialize();
153 
154     /* Create a packet pool.  */
155     status = nx_packet_pool_create(&pool_0, "NetX Main Packet Pool", PACKET_SIZE, pointer, PACKET_POOL_SIZE);
156     pointer = pointer + PACKET_POOL_SIZE;
157 
158     if(status)
159         ASSERT_SUCCESS(status);
160 
161     /* Create an IP instance.  */
162     status = nx_ip_create(&ip_0, "NetX IP Instance 0", IP_ADDRESS(1, 2, 3, 4), 0xFFFFFF00UL, &pool_0, _netx_ptp_network_driver,
163                           pointer, 2048, 1);
164     pointer = pointer + 2048;
165 
166     /* Create another IP instance.  */
167     status += nx_ip_create(&ip_1, "NetX IP Instance 1", IP_ADDRESS(1, 2, 3, 5), 0xFFFFFF00UL, &pool_0, _nx_ram_network_driver,
168                            pointer, 2048, 1);
169     pointer = pointer + 2048;
170 
171     if(status)
172         ASSERT_SUCCESS(status);
173 
174     /* Enable ARP and supply ARP cache memory for IP Instance 0.  */
175     status = nx_arp_enable(&ip_0, (void *) pointer, 1024);
176     pointer = pointer + 1024;
177 
178     /* Enable ARP and supply ARP cache memory for IP Instance 1.  */
179     status += nx_arp_enable(&ip_1, (void *) pointer, 1024);
180     pointer = pointer + 1024;
181 
182     /* Check ARP enable status.  */
183     if(status)
184         ASSERT_SUCCESS(status);
185 
186     /* Enable UDP processing for both IP instances.  */
187     status = nx_udp_enable(&ip_0);
188     status += nx_udp_enable(&ip_1);
189 
190     /* Check UDP enable status.  */
191     if(status)
192         ASSERT_SUCCESS(status);
193 #endif
194 }
195 
196 
197 /* PTP handler.  */
ptp_event_callback(NX_PTP_CLIENT * ptp_client_ptr,UINT event,VOID * event_data,VOID * callback_data)198 static UINT ptp_event_callback(NX_PTP_CLIENT *ptp_client_ptr, UINT event, VOID *event_data, VOID *callback_data)
199 {
200 NX_PTP_DATE_TIME date;
201 
202     NX_PARAMETER_NOT_USED(callback_data);
203 
204     switch (event)
205     {
206         case NX_PTP_CLIENT_EVENT_MASTER:
207         {
208             DBGPRINTF("new MASTER clock!\r\n");
209             break;
210         }
211 
212         case NX_PTP_CLIENT_EVENT_SYNC:
213         {
214             nx_ptp_client_sync_info_get((NX_PTP_CLIENT_SYNC *)event_data, NX_NULL, &synced_utc_offset);
215             DBGPRINTF("SYNC event: utc offset=%d\r\n", synced_utc_offset);
216 
217             /* read the PTP clock */
218             nx_ptp_client_time_get(ptp_client_ptr, &synced_time);
219 
220             /* convert PTP time to UTC date and time */
221             nx_ptp_client_utility_convert_time_to_date(&synced_time, -synced_utc_offset, &date);
222 
223             /* display the current time */
224             DBGPRINTF("ts: %d%d.%d\r\n", synced_time.second_high,
225                                          synced_time.second_low,
226                                          synced_time.nanosecond);
227             DBGPRINTF("%2u/%02u/%u %02u:%02u:%02u.%09lu\r\n", date.day, date.month, date.year,
228                                                               date.hour, date.minute, date.second,
229                                                               date.nanosecond);
230 
231             break;
232         }
233 
234         case NX_PTP_CLIENT_EVENT_TIMEOUT:
235         {
236             DBGPRINTF("Master clock TIMEOUT!\r\n");
237             break;
238         }
239         default:
240         {
241             break;
242         }
243     }
244 
245     return(0);
246 }
247 
248 /* Hijack callback function to modify timestamp.  */
clock_callback(NX_PTP_CLIENT * client_ptr,UINT operation,NX_PTP_TIME * time_ptr,NX_PACKET * packet_ptr,VOID * callback_data)249 static UINT clock_callback(NX_PTP_CLIENT *client_ptr, UINT operation,
250                            NX_PTP_TIME *time_ptr, NX_PACKET *packet_ptr,
251                            VOID *callback_data)
252 {
253     if (operation == NX_PTP_CLIENT_CLOCK_PACKET_TS_PREPARE)
254     {
255 
256         /* Update DelayRequest timestamp. */
257         clock_callback(client_ptr, NX_PTP_CLIENT_CLOCK_SET,
258                        &test_timestamps[test_index].delay_request, NX_NULL, callback_data);
259     }
260 
261 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
262     return(_netx_ptp_clock_callback(client_ptr, operation, time_ptr, packet_ptr, callback_data));
263 #else
264     return(nx_ptp_client_soft_clock_callback(client_ptr, operation, time_ptr, packet_ptr, callback_data));
265 #endif
266 }
267 
268 /* Define the test threads.  */
ntest_0_entry(ULONG thread_input)269 static void    ntest_0_entry(ULONG thread_input)
270 {
271 UINT status;
272 
273     /* Create the PTP client instance */
274     status = nx_ptp_client_create(&ptp_client, &ip_0, 0, &pool_0,
275                                   2, ptp_stack, sizeof(ptp_stack),
276                                   clock_callback, NX_NULL);
277     ASSERT_SUCCESS(status);
278 
279     for (test_index = 0; test_index < sizeof(test_timestamps) / sizeof(TEST_TIMESTAMP); test_index++)
280     {
281 
282         /* Start the PTP client */
283         status = nx_ptp_client_start(&ptp_client, NX_NULL, 0, 0, 0, ptp_event_callback, NX_NULL);
284         ASSERT_SUCCESS(status);
285 
286         /* Reset synced time. */
287         memset(&synced_time, 0, sizeof(synced_time));
288         synced_utc_offset = 0xFFFF;
289 
290         /* Set expected value. */
291         expected_utc_offset = 0x1234 + test_index;
292         calibrate_timestamp(&test_timestamps[test_index], &expected_time);
293 
294         /* Wake up server thread.  */
295         tx_thread_resume(&ntest_1);
296 
297         /* Sleep 5 seconds for sync up. */
298         tx_thread_sleep(5 * NX_IP_PERIODIC_RATE);
299 
300         /* Compare synced time. */
301         ASSERT_TRUE(synced_utc_offset == expected_utc_offset);
302         ASSERT_SUCCESS(memcmp(&synced_time, &expected_time, sizeof(expected_time)));
303 
304         /* Stop the PTP client */
305         status = nx_ptp_client_stop(&ptp_client);
306         ASSERT_SUCCESS(status);
307     }
308 
309     printf("SUCCESS!\n");
310     test_control_return(0);
311 }
312 
313 /* This thread acts as PTP server, accepting the connection. */
ntest_1_entry(ULONG thread_input)314 static void    ntest_1_entry(ULONG thread_input)
315 {
316 DELAY_REQUEST_CONTEXT context;
317 UINT status;
318 UINT i;
319 UINT priority;
320 
321     create_socket(&ip_1, &generic_socket, PTP_GENERAL_UDP_PORT);
322     create_socket(&ip_1, &event_socket, PTP_EVENT_UDP_PORT);
323 
324     for (i = 0; i < sizeof(test_timestamps) / sizeof(TEST_TIMESTAMP); i++)
325     {
326 
327         /* Suspend current thread and wait for client thread. */
328         tx_thread_suspend(tx_thread_identify());
329 
330         /* Sleep 1 second to wait for PTP thread running. */
331         tx_thread_sleep(1 * NX_IP_PERIODIC_RATE);
332 
333         /* Send announce.  */
334         send_announce(&generic_socket, &pool_0, expected_utc_offset);
335 
336         /* Set the timestamp of FollowUp received.  */
337         clock_callback(&ptp_client, NX_PTP_CLIENT_CLOCK_SET,
338                        &test_timestamps[i].sync_received, NX_NULL, NX_NULL);
339 
340         /* Send sync.  */
341         send_sync(&event_socket, &pool_0, &test_timestamps[i].sync);
342 
343         /* Send follow up.  */
344         send_follow_up(&generic_socket, &pool_0, &test_timestamps[i].follow_up);
345 
346         /* Wait for delay request.  */
347         status = receive_delay_request(&event_socket, &context, NX_WAIT_FOREVER);
348         ASSERT_SUCCESS(status);
349 
350         /* Send delay response.  */
351         send_delay_response(&generic_socket, &pool_0, &context, &test_timestamps[i].delay_response);
352     }
353 }