]> git.ipfire.org Git - thirdparty/shairport-sync.git/blob - ptp-utilities.c
Update check_classic_mac_basic.yml
[thirdparty/shairport-sync.git] / ptp-utilities.c
1 /*
2 * This file is part of Shairport Sync.
3 * Copyright (c) Mike Brady 2020 -- 2023
4 * All rights reserved.
5 *
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:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
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.
25 */
26
27 #include "definitions.h"
28 #include <arpa/inet.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <pthread.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/mman.h>
36 #ifdef COMPILE_FOR_FREEBSD
37 #include <netinet/in.h>
38 #include <sys/socket.h>
39 #endif
40 #define __STDC_FORMAT_MACROS
41 #include "common.h"
42 #include "ptp-utilities.h"
43 #include <inttypes.h>
44 #include <unistd.h>
45
46 int shm_fd;
47 void *mapped_addr = NULL;
48
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
55
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:
58
59 // We ensure that the secondary record is written by NQPTP _strictly after_
60 // all writes to the main record are complete.
61
62 // Here we read two copies of the entire record, the second
63 // _strictly after_ all reads from the first are complete.
64
65 // (Strict write and read ordering is ensured using the __sync_synchronize() construct.)
66
67 // We then compare the main record in the first read to the
68 // secondary record in the second read.
69
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.
72
73 if ((mapped_addr != MAP_FAILED) && (mapped_addr != NULL)) {
74 int loop_count = 1;
75 do {
76 __sync_synchronize();
77 memcpy(nqptp_data, (char *)mapped_addr, sizeof(struct shm_structure));
78 __sync_synchronize();
79 // read again strictly after a full read -- this is to read the secondary strictly after the
80 // primary
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
85 loop_count++;
86 }
87 } while (
88 (memcmp(&nqptp_data->main, &local_nqptp_data.secondary, sizeof(shm_structure_set)) != 0) &&
89 (loop_count < 10));
90 if (loop_count == 10) {
91 debug(1, "get_nqptp_data -- main and secondary records don't match after %d attempts!",
92 loop_count);
93 response = -1;
94 } else {
95 response = 0;
96 }
97 } else {
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");
102 else
103 debug(1, "get_nqptp_data failed");
104 }
105 // int64_t et = get_absolute_time_in_ns() - tn;
106 // debug(1, "get_nqptp_data time: %.3f microseconds.", 0.001 * et);
107 return response;
108 }
109
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;
115 }
116 return response;
117 }
118
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)
125 *raw_offset = 0;
126 if (time_of_sample != NULL)
127 *time_of_sample = 0;
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;
144 } else {
145 response = clock_no_master;
146 }
147 } else {
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;
152 else
153 response = clock_version_mismatch;
154 }
155 } else {
156 response = clock_data_unavailable;
157 }
158 return response;
159 }
160
161 int ptp_shm_interface_open() {
162 int response = 0;
163 debug(2, "ptp_shm_interface_open with mapped_addr = %" PRIuPTR "", mapped_addr);
164 if ((mapped_addr == NULL) || (mapped_addr == MAP_FAILED)) {
165 response = -1;
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");
170
171 if (strcmp(config.nqptp_shared_memory_interface_name, "") != 0) {
172 response = 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) {
176 mapped_addr =
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) {
182 response = -1;
183 }
184 if (close(shared_memory_file_descriptor) == -1) {
185 response = -1;
186 }
187 } else {
188 response = -1;
189 }
190 } else {
191 debug(1, "No config.nqptp_shared_memory_interface_name");
192 }
193 if (response == 0)
194 debug(2, "ptp_shm_interface_open -- success!");
195 else
196 debug(2, "ptp_shm_interface_open -- fail!");
197 } else {
198 debug(2, "ptp_shm_interface_open -- already open!");
199 }
200 return response;
201 }
202
203 int ptp_shm_interface_close() {
204 int response = -1;
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));
208 if (response != 0)
209 debug(1, "error unmapping shared memory.");
210 }
211 mapped_addr = NULL;
212 return response;
213 }
214
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,
222 msg);
223 debug(2, "Send control message to NQPTP: \"%s\"", full_message);
224 int s;
225 unsigned short port = htons(NQPTP_CONTROL_PORT);
226 struct sockaddr_in server;
227
228 /* Create a datagram socket in the internet domain and use the
229 * default protocol (UDP).
230 */
231 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
232 die("Can't open a socket to NQPTP");
233 }
234
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 */
239
240 /* Send the message in buf to the server */
241 if (sendto(s, full_message, full_message_size, 0, (struct sockaddr *)&server, sizeof(server)) <
242 0) {
243 die("error sending timing_peer_list to NQPTP");
244 }
245 /* Deallocate the socket */
246 close(s);
247
248 /* deallocate the message string */
249 free(full_message);
250 } else {
251 debug(1, "Couldn't allocate memory to prepare a qualified ptp control message string.");
252 }
253 }