]> git.ipfire.org Git - thirdparty/shairport-sync.git/blame - ptp-utilities.c
Update check_classic_systemd_full.yml
[thirdparty/shairport-sync.git] / ptp-utilities.c
CommitLineData
0af7f4fb
MB
1/*
2 * This file is part of Shairport Sync.
dabfee5b 3 * Copyright (c) Mike Brady 2020 -- 2023
0af7f4fb
MB
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
e925e196 27#include "definitions.h"
0af7f4fb
MB
28#include <arpa/inet.h>
29#include <errno.h>
30#include <fcntl.h>
31#include <pthread.h>
32#include <stdio.h>
966d68bd 33#include <stdlib.h>
0af7f4fb
MB
34#include <string.h>
35#include <sys/mman.h>
e925e196 36#ifdef COMPILE_FOR_FREEBSD
e925e196 37#include <netinet/in.h>
c0a3dacf 38#include <sys/socket.h>
e925e196 39#endif
0af7f4fb
MB
40#define __STDC_FORMAT_MACROS
41#include "common.h"
fd880056 42#include "ptp-utilities.h"
0af7f4fb
MB
43#include <inttypes.h>
44#include <unistd.h>
45
0af7f4fb 46int shm_fd;
73766b07 47void *mapped_addr = NULL;
0af7f4fb 48
0af7f4fb
MB
49// returns a copy of the shared memory data from the nqptp
50// shared memory interface, so long as it's open.
51int get_nqptp_data(struct shm_structure *nqptp_data) {
dabfee5b
MB
52 // uint64_t tn = get_absolute_time_in_ns(); // if interested in timing the function...
53 struct shm_structure local_nqptp_data;
0af7f4fb 54 int response = -1; // presume the worst. Fix it on success
dabfee5b
MB
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.
0af7f4fb
MB
72
73 if ((mapped_addr != MAP_FAILED) && (mapped_addr != NULL)) {
dabfee5b
MB
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 < 100));
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 }
341f67ac
MB
97 } else {
98 if (mapped_addr == NULL)
dabfee5b 99 debug(1, "get_nqptp_data failed because the mapped_addr is NULL");
341f67ac 100 else if (mapped_addr == MAP_FAILED)
dabfee5b 101 debug(1, "get_nqptp_data failed because the mapped_addr is MAP_FAILED");
341f67ac
MB
102 else
103 debug(1, "get_nqptp_data failed");
0af7f4fb 104 }
dabfee5b
MB
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
110int 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 }
0af7f4fb
MB
116 return response;
117}
118
861455f7 119int ptp_get_clock_info(uint64_t *actual_clock_id, uint64_t *time_of_sample, uint64_t *raw_offset,
a109b587 120 uint64_t *mastership_start_time) {
04c7f845 121 int response = clock_ok;
0af7f4fb
MB
122 if (actual_clock_id != NULL)
123 *actual_clock_id = 0;
124 if (raw_offset != NULL)
125 *raw_offset = 0;
861455f7
MB
126 if (time_of_sample != NULL)
127 *time_of_sample = 0;
128 if (mastership_start_time != NULL)
129 *mastership_start_time = 0;
341f67ac
MB
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
dabfee5b 135 if (nqptp_data.main.master_clock_id != 0) {
341f67ac 136 if (actual_clock_id != NULL)
dabfee5b 137 *actual_clock_id = nqptp_data.main.master_clock_id;
341f67ac 138 if (time_of_sample != NULL)
dabfee5b 139 *time_of_sample = nqptp_data.main.local_time;
341f67ac 140 if (raw_offset != NULL)
dabfee5b 141 *raw_offset = nqptp_data.main.local_to_master_time_offset;
341f67ac 142 if (mastership_start_time != NULL)
dabfee5b 143 *mastership_start_time = nqptp_data.main.master_clock_start_time;
0af7f4fb 144 } else {
341f67ac 145 response = clock_no_master;
0af7f4fb 146 }
04c7f845 147 } else {
341f67ac
MB
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;
0af7f4fb 154 }
0af7f4fb 155 } else {
341f67ac 156 response = clock_data_unavailable;
0af7f4fb 157 }
0af7f4fb
MB
158 return response;
159}
160
161int ptp_shm_interface_open() {
341f67ac 162 int response = 0;
73766b07 163 debug(2, "ptp_shm_interface_open with mapped_addr = %" PRIuPTR "", mapped_addr);
322f0772 164 if ((mapped_addr == NULL) || (mapped_addr == MAP_FAILED)) {
341f67ac 165 response = -1;
73766b07
MB
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");
a382ac66 170
341f67ac
MB
171 if (strcmp(config.nqptp_shared_memory_interface_name, "") != 0) {
172 response = 0;
173 int shared_memory_file_descriptor =
dabfee5b 174 shm_open(config.nqptp_shared_memory_interface_name, O_RDONLY, 0);
341f67ac
MB
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
dabfee5b 179 mmap(NULL, sizeof(struct shm_structure), PROT_READ, MAP_SHARED,
341f67ac
MB
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 {
35317687
MB
188 response = -1;
189 }
190 } else {
341f67ac 191 debug(1, "No config.nqptp_shared_memory_interface_name");
0af7f4fb 192 }
73766b07
MB
193 if (response == 0)
194 debug(2, "ptp_shm_interface_open -- success!");
195 else
196 debug(2, "ptp_shm_interface_open -- fail!");
04c7f845 197 } else {
18ed1bda 198 debug(2, "ptp_shm_interface_open -- already open!");
0af7f4fb
MB
199 }
200 return response;
201}
202
203int ptp_shm_interface_close() {
204 int response = -1;
205 if ((mapped_addr != MAP_FAILED) && (mapped_addr != NULL)) {
ace5537a 206 debug(2, "ptp_shm_interface_close");
0af7f4fb
MB
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
215void ptp_send_control_message_string(const char *msg) {
966d68bd
MB
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);
04aba03b 223 debug(2, "Send control message to NQPTP: \"%s\"", full_message);
966d68bd
MB
224 int s;
225 unsigned short port = htons(NQPTP_CONTROL_PORT);
226 struct sockaddr_in server;
0af7f4fb 227
966d68bd
MB
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 }
0af7f4fb 234
966d68bd
MB
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 */
0af7f4fb 239
966d68bd
MB
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.");
0af7f4fb 252 }
0af7f4fb 253}