1 /*
2 This is a small demo of the NetX SNTP Client on the high-performance NetX TCP/IP stack.
3 This demo relies on Thread, NetX and NetX SNTP Client API to execute the Simple Network Time
4 Protocol in unicast and broadcast modes.
5
6 */
7
8
9 #include <stdio.h>
10 #include "nx_api.h"
11 #include "nx_ip.h"
12 #include "nxd_sntp_client.h"
13
14 /* Define SNTP packet size. */
15 #define NX_SNTP_CLIENT_PACKET_SIZE (NX_UDP_PACKET + 100)
16
17 /* Define SNTP packet pool size. */
18 #define NX_SNTP_CLIENT_PACKET_POOL_SIZE (4 * (NX_SNTP_CLIENT_PACKET_SIZE + sizeof(NX_PACKET)))
19
20 /* Define how often the demo checks for SNTP updates. */
21 #define DEMO_PERIODIC_CHECK_INTERVAL (1 * NX_IP_PERIODIC_RATE)
22
23 /* Define how often we check on SNTP server status. We expect updates from the SNTP
24 server about every hour using the SNTP Client defaults. For testing make this (much) shorter. */
25 #define CHECK_SNTP_UPDATES_TIMEOUT (180 * NX_IP_PERIODIC_RATE)
26
27 /* Set up generic network driver for demo program. */
28 void _nx_ram_network_driver(struct NX_IP_DRIVER_STRUCT *driver_req);
29
30 /* Application defined services of the NetX SNTP Client. */
31
32 UINT leap_second_handler(NX_SNTP_CLIENT *client_ptr, UINT leap_indicator);
33 UINT kiss_of_death_handler(NX_SNTP_CLIENT *client_ptr, UINT KOD_code);
34 VOID time_update_callback(NX_SNTP_TIME_MESSAGE *time_update_ptr, NX_SNTP_TIME *local_time);
35
36
37 /* Set up client thread and network resources. */
38
39 NX_PACKET_POOL client_packet_pool;
40 NX_IP client_ip;
41 TX_THREAD demo_client_thread;
42 NX_SNTP_CLIENT demo_sntp_client;
43 TX_EVENT_FLAGS_GROUP sntp_flags;
44
45 #define DEMO_SNTP_UPDATE_EVENT 1
46
47 /* Configure the SNTP Client to use IPv6. If not enabled, the
48 Client will use IPv4. Note: IPv6 must be enabled in NetX Duo
49 for the Client to communicate over IPv6. */
50 #ifdef FEATURE_NX_IPV6
51 /* #define USE_IPV6 */
52 #endif /* FEATURE_NX_IPV6 */
53
54
55 /* Configure the SNTP Client to use unicast SNTP. */
56 #define USE_UNICAST
57
58
59 #define CLIENT_IP_ADDRESS IP_ADDRESS(192,2,2,66)
60 #define SERVER_IP_ADDRESS IP_ADDRESS(192,2,2,92)
61 #define SERVER_IP_ADDRESS_2 SERVER_IP_ADDRESS
62
63 /* Set up the SNTP network and address index; */
64 UINT iface_index =0;
65 UINT prefix = 64;
66 UINT address_index;
67
68 /* Set up client thread entry point. */
69 void demo_client_thread_entry(ULONG info);
70
71 /* Define main entry point. */
main()72 int main()
73 {
74 /* Enter the ThreadX kernel. */
75 tx_kernel_enter();
76 return 0;
77 }
78
79 /* Define what the initial system looks like. */
tx_application_define(void * first_unused_memory)80 void tx_application_define(void *first_unused_memory)
81 {
82
83 UINT status;
84 UCHAR *free_memory_pointer;
85
86
87 free_memory_pointer = (UCHAR *)first_unused_memory;
88
89 /* Create client packet pool. */
90 status = nx_packet_pool_create(&client_packet_pool, "SNTP Client Packet Pool",
91 NX_SNTP_CLIENT_PACKET_SIZE, free_memory_pointer,
92 NX_SNTP_CLIENT_PACKET_POOL_SIZE);
93
94 /* Check for errors. */
95 if (status != NX_SUCCESS)
96 {
97
98 return;
99 }
100
101 /* Initialize the NetX system. */
102 nx_system_initialize();
103
104 /* Update pointer to unallocated (free) memory. */
105 free_memory_pointer = free_memory_pointer + NX_SNTP_CLIENT_PACKET_POOL_SIZE;
106
107 /* Create Client IP instances */
108 status = nx_ip_create(&client_ip, "SNTP IP Instance", CLIENT_IP_ADDRESS,
109 0xFFFFFF00UL, &client_packet_pool, _nx_ram_network_driver,
110 free_memory_pointer, 2048, 1);
111
112 /* Check for error. */
113 if (status != NX_SUCCESS)
114 {
115
116 return;
117 }
118
119 free_memory_pointer = free_memory_pointer + 2048;
120
121 #ifndef NX_DISABLE_IPV4
122 /* Enable ARP and supply ARP cache memory. */
123 status = nx_arp_enable(&client_ip, (void **) free_memory_pointer, 2048);
124
125 /* Check for error. */
126 if (status != NX_SUCCESS)
127 {
128
129 return;
130 }
131 #endif /* NX_DISABLE_IPV4 */
132
133 /* Update pointer to unallocated (free) memory. */
134 free_memory_pointer = free_memory_pointer + 2048;
135
136 /* Enable UDP for client. */
137 status = nx_udp_enable(&client_ip);
138
139 /* Check for error. */
140 if (status != NX_SUCCESS)
141 {
142
143 return;
144 }
145
146 #ifndef NX_DISABLE_IPV4
147 status = nx_icmp_enable(&client_ip);
148
149 /* Check for error. */
150 if (status != NX_SUCCESS)
151 {
152
153 return;
154 }
155 #endif /* NX_DISABLE_IPV4 */
156
157 /* Create the client thread */
158 status = tx_thread_create(&demo_client_thread, "SNTP Client Thread", demo_client_thread_entry,
159 (ULONG)(&demo_sntp_client), free_memory_pointer, 2048,
160 4, 4, TX_NO_TIME_SLICE, TX_DONT_START);
161
162 /* Check for errors */
163 if (status != TX_SUCCESS)
164 {
165
166 return;
167 }
168
169 /* Create the event flags. */
170 status = tx_event_flags_create(&sntp_flags, "SNTP event flags");
171
172 /* Check for errors */
173 if (status != TX_SUCCESS)
174 {
175
176 return;
177 }
178
179 /* Update pointer to unallocated (free) memory. */
180 free_memory_pointer = free_memory_pointer + 2048;
181
182 /* set the SNTP network interface to the primary interface. */
183 iface_index = 0;
184
185 /* Create the SNTP Client to run in broadcast mode.. */
186 status = nx_sntp_client_create(&demo_sntp_client, &client_ip, iface_index, &client_packet_pool,
187 leap_second_handler,
188 kiss_of_death_handler,
189 NULL /* no random_number_generator callback */);
190
191 /* Check for error. */
192 if (status != NX_SUCCESS)
193 {
194
195 /* Bail out!*/
196 return;
197 }
198
199 tx_thread_resume(&demo_client_thread);
200
201 return;
202 }
203
204 /* Define size of buffer to display client's local time. */
205 #define BUFSIZE 50
206
207 /* Define the client thread. */
demo_client_thread_entry(ULONG info)208 void demo_client_thread_entry(ULONG info)
209 {
210
211 UINT status;
212 UINT spin;
213 UINT server_status;
214 ULONG base_seconds;
215 ULONG base_fraction;
216 ULONG seconds, milliseconds, microseconds, fraction;
217 UINT wait = 0;
218 UINT error_counter = 0;
219 ULONG events = 0;
220 #ifdef USE_IPV6
221 NXD_ADDRESS sntp_server_address;
222 NXD_ADDRESS client_ip_address;
223 #endif
224
225 NX_PARAMETER_NOT_USED(info);
226
227 /* Give other threads (IP instance) initialize first. */
228 tx_thread_sleep(NX_IP_PERIODIC_RATE);
229
230 #ifdef USE_IPV6
231 /* Set up IPv6 services. */
232 status = nxd_ipv6_enable(&client_ip);
233
234 status += nxd_icmp_enable(&client_ip);
235
236 if (status != NX_SUCCESS)
237 return;
238
239 client_ip_address.nxd_ip_address.v6[0] = 0x20010db8;
240 client_ip_address.nxd_ip_address.v6[1] = 0x0000f101;
241 client_ip_address.nxd_ip_address.v6[2] = 0x0;
242 client_ip_address.nxd_ip_address.v6[3] = 0x101;
243 client_ip_address.nxd_ip_version = NX_IP_VERSION_V6;
244
245 /* Set the IPv6 server address. */
246 sntp_server_address.nxd_ip_address.v6[0] = 0x20010db8;
247 sntp_server_address.nxd_ip_address.v6[1] = 0x0000f101;
248 sntp_server_address.nxd_ip_address.v6[2] = 0x0;
249 sntp_server_address.nxd_ip_address.v6[3] = 0x00000106;
250 sntp_server_address.nxd_ip_version = NX_IP_VERSION_V6;
251
252 /* Establish the link local address for the host. The RAM driver creates
253 a virtual MAC address. */
254 status = nxd_ipv6_address_set(&client_ip, iface_index, NX_NULL, 10, NULL);
255
256 /* Check for link local address set error. */
257 if (status != NX_SUCCESS)
258 {
259 return;
260 }
261
262 /* Set the host global IP address. We are assuming a 64
263 bit prefix here but this can be any value (< 128). */
264 status = nxd_ipv6_address_set(&client_ip, iface_index, &client_ip_address, prefix, &address_index);
265
266 /* Check for global address set error. */
267 if (status != NX_SUCCESS)
268 {
269 return;
270 }
271
272 /* Wait while NetX Duo validates the global and link local addresses. */
273 tx_thread_sleep(5 * NX_IP_PERIODIC_RATE);
274
275 #endif
276
277 /* Setup time update callback function. */
278 nx_sntp_client_set_time_update_notify(&demo_sntp_client, time_update_callback);
279
280 /* Set up client time updates depending on mode. */
281 #ifdef USE_UNICAST
282
283 /* Initialize the Client for unicast mode to poll the SNTP server once an hour. */
284 #ifdef USE_IPV6
285 /* Use the duo service to set up the Client and set the IPv6 SNTP server. Note: this
286 can take either an IPv4 or IPv6 address. */
287 status = nxd_sntp_client_initialize_unicast(&demo_sntp_client, &sntp_server_address);
288 #else
289 /* Use the IPv4 service to set up the Client and set the IPv4 SNTP server. */
290 status = nx_sntp_client_initialize_unicast(&demo_sntp_client, SERVER_IP_ADDRESS);
291 #endif /* USE_IPV6 */
292
293
294 #else /* Broadcast mode */
295
296 /* Initialize the Client for broadcast mode, no roundtrip calculation required and a broadcast SNTP service. */
297 #ifdef USE_IPV6
298 /* Use the duo service to initialize the Client and set IPv6 SNTP all hosts multicast address.
299 (Note: This can take either an IPv4 or IPv6 address.)*/
300 status = nxd_sntp_client_initialize_broadcast(&demo_sntp_client, &sntp_server_address, NX_NULL);
301 #else
302
303 /* Use the IPv4 service to initialize the Client and set IPv4 SNTP broadcast address. */
304 status = nx_sntp_client_initialize_broadcast(&demo_sntp_client, NX_NULL, SERVER_IP_ADDRESS);
305 #endif /* USE_IPV6 */
306 #endif /* USE_UNICAST */
307
308 /* Check for error. */
309 if (status != NX_SUCCESS)
310 {
311 return;
312 }
313
314 /* Set the base time which is approximately the number of seconds since the turn of the last century.
315 If this is not available in SNTP format, the nx_sntp_client_utility_add_msecs_to_ntp_time service
316 can convert milliseconds to fraction. For how to compute NTP seconds from real time, read the
317 NetX SNTP User Guide.
318
319 Otherwise set the base time to zero and set NX_SNTP_CLIENT_IGNORE_MAX_ADJUST_STARTUP to NX_TRUE for
320 the SNTP CLient to accept the first time update without applying a minimum or maximum adjustment
321 parameters (NX_SNTP_CLIENT_MIN_TIME_ADJUSTMENT and NX_SNTP_CLIENT_MAX_TIME_ADJUSTMENT). */
322
323 base_seconds = 0xd2c96b90; /* Jan 24, 2012 UTC */
324 base_fraction = 0xa132db1e;
325
326 /* Apply to the SNTP Client local time. */
327 status = nx_sntp_client_set_local_time(&demo_sntp_client, base_seconds, base_fraction);
328
329 /* Check for error. */
330 if (status != NX_SUCCESS)
331 {
332 return;
333 }
334
335 /* Run whichever service the client is configured for. */
336 #ifdef USE_UNICAST
337 status = nx_sntp_client_run_unicast(&demo_sntp_client);
338 #else
339 status = nx_sntp_client_run_broadcast(&demo_sntp_client);
340 #endif /* USE_UNICAST */
341
342 if (status != NX_SUCCESS)
343 {
344 return;
345 }
346
347 spin = NX_TRUE;
348
349 /* Now check periodically for time changes. */
350 while(spin)
351 {
352 /* Wait for a server update event. */
353 tx_event_flags_get(&sntp_flags, DEMO_SNTP_UPDATE_EVENT, TX_OR_CLEAR, &events, DEMO_PERIODIC_CHECK_INTERVAL);
354
355 if (events == DEMO_SNTP_UPDATE_EVENT)
356 {
357
358 /* Check for valid SNTP server status. */
359 status = nx_sntp_client_receiving_updates(&demo_sntp_client, &server_status);
360
361 if ((status != NX_SUCCESS) || (server_status == NX_FALSE))
362 {
363
364 /* We do not have a valid update. Skip processing any time data. */
365
366 /* If this happens repeatedly, consider stopping the SNTP Client thread, picking another
367 SNTP server and resuming the SNTP Client thread task (more details about that in the
368 comments at the end of this function).
369
370 If SNTP Client configurable parameters are too restrictive, such as Max Adjustment, that may also cause
371 valid server updates to be rejected. Configurable parameters, however, cannot be changed at run time.*/
372
373 continue;
374 }
375
376 /* We have a valid update. Get the SNTP Client time. */
377 status = nx_sntp_client_get_local_time_extended(&demo_sntp_client, &seconds, &fraction, NX_NULL, 0);
378
379 /* Convert fraction to microseconds. */
380 nx_sntp_client_utility_fraction_to_usecs(fraction, µseconds);
381
382 milliseconds = ((microseconds + 500) / 1000);
383
384 if (status != NX_SUCCESS)
385 {
386 printf("Internal error with getting local time 0x%x\n", status);
387 error_counter++;
388 }
389 else
390 {
391 printf("\nSNTP updated\n");
392 printf("Time: %lu.%03lu sec.\r\n", seconds, milliseconds);
393 }
394
395 /* Clear all events in our event flag. */
396 events = 0;
397 }
398 else
399 {
400
401 /* No SNTP update event.
402
403 In the meantime, if we have an RTC we might want to check its notion of time.
404 In this demo, we simulate the passage of time on our 'RTC' really just the CPU counter,
405 assuming that seconds and milliseconds have previously been set to a base (starting) time
406 (as was the SNTP Client before running it) */
407
408 seconds += 1; /* This is the sleep time (1 second) so is pretty close to an RTC */
409 milliseconds += 1; /* We don't know this value but for demonstration purposes we change it */
410
411 /* Update our timer. */
412 wait += DEMO_PERIODIC_CHECK_INTERVAL;
413
414 /* Check if it is time to display the local 'RTC' time. */
415 if (wait >= CHECK_SNTP_UPDATES_TIMEOUT)
416 {
417 /* It is. Reset the timeout and print local time. */
418 wait = 0;
419
420 printf("Time: %lu.%03lu sec.\r\n", seconds, milliseconds);
421 }
422 }
423 }
424
425 /* We can stop the SNTP service if for example we think the SNTP server has stopped sending updates.
426
427 To restart the SNTP Client, simply call the nx_sntp_client_initialize_unicast or nx_sntp_client_initialize_broadcast
428 using another SNTP server IP address as input, and resume the SNTP Client by calling nx_sntp_client_run_unicast or
429 nx_sntp_client_run_braodcast. */
430 status = nx_sntp_client_stop(&demo_sntp_client);
431
432 if (status != NX_SUCCESS)
433 {
434 error_counter++;
435 }
436
437 /* When done with the SNTP Client, we delete it */
438 status = nx_sntp_client_delete(&demo_sntp_client);
439
440 return;
441 }
442
443
444 /* This application defined handler for handling an impending leap second is not
445 required by the SNTP Client. The default handler below only logs the event for
446 every time stamp received with the leap indicator set. */
447
leap_second_handler(NX_SNTP_CLIENT * client_ptr,UINT leap_indicator)448 UINT leap_second_handler(NX_SNTP_CLIENT *client_ptr, UINT leap_indicator)
449 {
450 NX_PARAMETER_NOT_USED(client_ptr);
451 NX_PARAMETER_NOT_USED(leap_indicator);
452
453 /* Handle the leap second handler... */
454
455 return NX_SUCCESS;
456 }
457
458 /* This application defined handler for handling a Kiss of Death packet is not
459 required by the SNTP Client. A KOD handler should determine
460 if the Client task should continue vs. abort sending/receiving time data
461 from its current time server, and if aborting if it should remove
462 the server from its active server list.
463
464 Note that the KOD list of codes is subject to change. The list
465 below is current at the time of this software release. */
466
kiss_of_death_handler(NX_SNTP_CLIENT * client_ptr,UINT KOD_code)467 UINT kiss_of_death_handler(NX_SNTP_CLIENT *client_ptr, UINT KOD_code)
468 {
469
470 UINT remove_server_from_list = NX_FALSE;
471 UINT status = NX_SUCCESS;
472
473 NX_PARAMETER_NOT_USED(client_ptr);
474
475 /* Handle kiss of death by code group. */
476 switch (KOD_code)
477 {
478
479 case NX_SNTP_KOD_RATE:
480 case NX_SNTP_KOD_NOT_INIT:
481 case NX_SNTP_KOD_STEP:
482
483 /* Find another server while this one is temporarily out of service. */
484 status = NX_SNTP_KOD_SERVER_NOT_AVAILABLE;
485
486 break;
487
488 case NX_SNTP_KOD_AUTH_FAIL:
489 case NX_SNTP_KOD_NO_KEY:
490 case NX_SNTP_KOD_CRYP_FAIL:
491
492 /* These indicate the server will not service client with time updates
493 without successful authentication. */
494
495
496 remove_server_from_list = NX_TRUE;
497
498 break;
499
500
501 default:
502
503 /* All other codes. Remove server before resuming time updates. */
504
505 remove_server_from_list = NX_TRUE;
506 break;
507 }
508
509 /* Removing the server from the active server list? */
510 if (remove_server_from_list)
511 {
512
513 /* Let the caller know it has to bail on this server before resuming service. */
514 status = NX_SNTP_KOD_REMOVE_SERVER;
515 }
516
517 return status;
518 }
519
520
521 /* This application defined handler for notifying SNTP time update event. */
522
time_update_callback(NX_SNTP_TIME_MESSAGE * time_update_ptr,NX_SNTP_TIME * local_time)523 VOID time_update_callback(NX_SNTP_TIME_MESSAGE *time_update_ptr, NX_SNTP_TIME *local_time)
524 {
525 NX_PARAMETER_NOT_USED(time_update_ptr);
526 NX_PARAMETER_NOT_USED(local_time);
527
528 tx_event_flags_set(&sntp_flags, DEMO_SNTP_UPDATE_EVENT, TX_OR);
529 }
530
531