]>
Commit | Line | Data |
---|---|---|
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 | 46 | int shm_fd; |
73766b07 | 47 | void *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. | |
51 | int 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 | ||
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 | } | |
0af7f4fb MB |
116 | return response; |
117 | } | |
118 | ||
861455f7 | 119 | int 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 | ||
161 | int 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 | ||
203 | int 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 | ||
215 | void 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 | } |