2 * This file is part of Shairport Sync.
3 * Copyright (c) Mike Brady 2020 -- 2023
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software and associated documentation
8 * files (the "Software"), to deal in the Software without
9 * restriction, including without limitation the rights to use,
10 * copy, modify, merge, publish, distribute, sublicense, and/or
11 * sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 * OTHER DEALINGS IN THE SOFTWARE.
27 #include "definitions.h"
28 #include <arpa/inet.h>
36 #ifdef COMPILE_FOR_FREEBSD
37 #include <netinet/in.h>
38 #include <sys/socket.h>
40 #define __STDC_FORMAT_MACROS
42 #include "ptp-utilities.h"
47 void *mapped_addr
= NULL
;
49 // returns a copy of the shared memory data from the nqptp
50 // shared memory interface, so long as it's open.
51 int get_nqptp_data(struct shm_structure
*nqptp_data
) {
52 // uint64_t tn = get_absolute_time_in_ns(); // if interested in timing the function...
53 struct shm_structure local_nqptp_data
;
54 int response
= -1; // presume the worst. Fix it on success
56 // We need to ensure that when we read the record, we are not reading it while it is partly
57 // updated and therefore inconsistent. To achieve this, we do the following:
59 // We ensure that the secondary record is written by NQPTP _strictly after_
60 // all writes to the main record are complete.
62 // Here we read two copies of the entire record, the second
63 // _strictly after_ all reads from the first are complete.
65 // (Strict write and read ordering is ensured using the __sync_synchronize() construct.)
67 // We then compare the main record in the first read to the
68 // secondary record in the second read.
70 // If they are equal, we can be sure we have not read a record that has been
71 // made inconsistent by an interrupted update.
73 if ((mapped_addr
!= MAP_FAILED
) && (mapped_addr
!= NULL
)) {
77 memcpy(nqptp_data
, (char *)mapped_addr
, sizeof(struct shm_structure
));
79 // read again strictly after a full read -- this is to read the secondary strictly after the
81 memcpy(&local_nqptp_data
, (char *)mapped_addr
, sizeof(struct shm_structure
));
82 // check that the main and secondary data sets match
83 if (memcmp(&nqptp_data
->main
, &local_nqptp_data
.secondary
, sizeof(shm_structure_set
)) != 0) {
84 usleep(2); // microseconds
88 (memcmp(&nqptp_data
->main
, &local_nqptp_data
.secondary
, sizeof(shm_structure_set
)) != 0) &&
90 if (loop_count
== 10) {
91 debug(1, "get_nqptp_data -- main and secondary records don't match after %d attempts!",
98 if (mapped_addr
== NULL
)
99 debug(1, "get_nqptp_data failed because the mapped_addr is NULL");
100 else if (mapped_addr
== MAP_FAILED
)
101 debug(1, "get_nqptp_data failed because the mapped_addr is MAP_FAILED");
103 debug(1, "get_nqptp_data failed");
105 // int64_t et = get_absolute_time_in_ns() - tn;
106 // debug(1, "get_nqptp_data time: %.3f microseconds.", 0.001 * et);
110 int ptp_get_clock_version() {
111 int response
= 0; // no version number information available
112 struct shm_structure nqptp_data
;
113 if (get_nqptp_data(&nqptp_data
) == 0) {
114 response
= nqptp_data
.version
;
119 int ptp_get_clock_info(uint64_t *actual_clock_id
, uint64_t *time_of_sample
, uint64_t *raw_offset
,
120 uint64_t *mastership_start_time
) {
121 int response
= clock_ok
;
122 if (actual_clock_id
!= NULL
)
123 *actual_clock_id
= 0;
124 if (raw_offset
!= NULL
)
126 if (time_of_sample
!= NULL
)
128 if (mastership_start_time
!= NULL
)
129 *mastership_start_time
= 0;
130 // if (ptp_shm_interface_open() == 0) {
131 struct shm_structure nqptp_data
;
132 if (get_nqptp_data(&nqptp_data
) == 0) {
133 if (nqptp_data
.version
== NQPTP_SHM_STRUCTURES_VERSION
) {
134 // assuming a clock id can not be zero
135 if (nqptp_data
.main
.master_clock_id
!= 0) {
136 if (actual_clock_id
!= NULL
)
137 *actual_clock_id
= nqptp_data
.main
.master_clock_id
;
138 if (time_of_sample
!= NULL
)
139 *time_of_sample
= nqptp_data
.main
.local_time
;
140 if (raw_offset
!= NULL
)
141 *raw_offset
= nqptp_data
.main
.local_to_master_time_offset
;
142 if (mastership_start_time
!= NULL
)
143 *mastership_start_time
= nqptp_data
.main
.master_clock_start_time
;
145 response
= clock_no_master
;
148 // the version can not be zero. If zero is returned here, it means that the shared memory is
149 // not yet initialised, so not availalbe
150 if (nqptp_data
.version
== 0)
151 response
= clock_service_unavailable
;
153 response
= clock_version_mismatch
;
156 response
= clock_data_unavailable
;
161 int ptp_shm_interface_open() {
163 debug(2, "ptp_shm_interface_open with mapped_addr = %" PRIuPTR
"", mapped_addr
);
164 if ((mapped_addr
== NULL
) || (mapped_addr
== MAP_FAILED
)) {
166 if (mapped_addr
== NULL
)
167 debug(3, "ptp_shm_interface_open is NULL");
168 if (mapped_addr
== MAP_FAILED
)
169 debug(3, "ptp_shm_interface_open is MAP_FAILED");
171 if (strcmp(config
.nqptp_shared_memory_interface_name
, "") != 0) {
173 int shared_memory_file_descriptor
=
174 shm_open(config
.nqptp_shared_memory_interface_name
, O_RDONLY
, 0);
175 if (shared_memory_file_descriptor
>= 0) {
177 // needs to be PROT_READ | PROT_WRITE to allow the mapped memory to be writable for the
178 // mutex to lock and unlock
179 mmap(NULL
, sizeof(struct shm_structure
), PROT_READ
, MAP_SHARED
,
180 shared_memory_file_descriptor
, 0);
181 if (mapped_addr
== MAP_FAILED
) {
184 if (close(shared_memory_file_descriptor
) == -1) {
191 debug(1, "No config.nqptp_shared_memory_interface_name");
194 debug(2, "ptp_shm_interface_open -- success!");
196 debug(2, "ptp_shm_interface_open -- fail!");
198 debug(2, "ptp_shm_interface_open -- already open!");
203 int ptp_shm_interface_close() {
205 if ((mapped_addr
!= MAP_FAILED
) && (mapped_addr
!= NULL
)) {
206 debug(2, "ptp_shm_interface_close");
207 response
= munmap(mapped_addr
, sizeof(struct shm_structure
));
209 debug(1, "error unmapping shared memory.");
215 void ptp_send_control_message_string(const char *msg
) {
216 size_t full_message_size
=
217 strlen(config
.nqptp_shared_memory_interface_name
) + strlen(" ") + strlen(msg
) + 1;
218 char *full_message
= malloc(full_message_size
);
219 if (full_message
!= NULL
) {
220 *full_message
= '\0';
221 snprintf(full_message
, full_message_size
, "%s %s", config
.nqptp_shared_memory_interface_name
,
223 debug(2, "Send control message to NQPTP: \"%s\"", full_message
);
225 unsigned short port
= htons(NQPTP_CONTROL_PORT
);
226 struct sockaddr_in server
;
228 /* Create a datagram socket in the internet domain and use the
229 * default protocol (UDP).
231 if ((s
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0) {
232 die("Can't open a socket to NQPTP");
235 /* Set up the server name */
236 server
.sin_family
= AF_INET
; /* Internet Domain */
237 server
.sin_port
= port
; /* Server Port */
238 server
.sin_addr
.s_addr
= 0; /* Server's Address */
240 /* Send the message in buf to the server */
241 if (sendto(s
, full_message
, full_message_size
, 0, (struct sockaddr
*)&server
, sizeof(server
)) <
243 die("error sending timing_peer_list to NQPTP");
245 /* Deallocate the socket */
248 /* deallocate the message string */
251 debug(1, "Couldn't allocate memory to prepare a qualified ptp control message string.");