]> git.ipfire.org Git - thirdparty/nqptp.git/commitdiff
Adding more multi-client functionality (still incomplete) but keeping the original...
authorMike Brady <4265913+mikebrady@users.noreply.github.com>
Mon, 27 Dec 2021 16:06:10 +0000 (16:06 +0000)
committerMike Brady <4265913+mikebrady@users.noreply.github.com>
Mon, 27 Dec 2021 16:06:10 +0000 (16:06 +0000)
nqptp-clock-sources.c
nqptp-clock-sources.h
nqptp-message-handlers.c
nqptp-message-handlers.h
nqptp-shm-structures.h
nqptp.c
nqptp.h

index 0f0ae082a47dfbca023231b137f9d16a09b40d66..ede9f3da4e6cd5c1f05e102a085076ccfc92aae8 100644 (file)
 #include <netdb.h>
 #include <string.h>
 #include <sys/socket.h>
-#include <sys/types.h>
+#include <sys/types.h> // for ftruncate and others
+#include <unistd.h>    // for ftruncate and others
+
+#include <fcntl.h>      /* For O_* constants */
+#include <sys/mman.h>   // for shared memory stuff
+#include <sys/select.h> // for fd_set
+#include <sys/stat.h>   // umask
 
 #ifdef CONFIG_FOR_FREEBSD
 #include <netinet/in.h>
 #endif
 
 clock_source_private_data clocks_private[MAX_CLOCKS];
+client_record clients[MAX_CLIENTS];
+
+int find_client_id(char *client_shared_memory_interface_name) {
+  int response = -1; // signify not found
+  if (client_shared_memory_interface_name != NULL) {
+    int i = 0;
+    // first, see if yu can find it anywhere
+    while ((response == -1) && (i < MAX_CLIENTS)) {
+      if (strcmp(clients[i].shm_interface_name, client_shared_memory_interface_name) == 0)
+        response = i;
+      else
+        i++;
+    }
+  }
+  return response;
+}
+
+int get_client_id(char *client_shared_memory_interface_name) {
+  int response = -1; // signify not found
+  if (client_shared_memory_interface_name != NULL) {
+    int i = 0;
+    // first, see if yu can find it anywhere
+    while ((response == -1) && (i < MAX_CLIENTS)) {
+      if (strcmp(clients[i].shm_interface_name, client_shared_memory_interface_name) == 0)
+        response = i;
+      else
+        i++;
+    }
+
+    if (response == -1) { // no match, so create one
+      i = 0;
+      while ((response == -1) && (i < MAX_CLIENTS)) {
+        if (clients[i].shm_interface_name[0] == '\0')
+          response = i;
+        else
+          i++;
+      }
+      if (response != -1) {
+        pthread_mutexattr_t shared;
+        int err;
+        strncpy(clients[i].shm_interface_name, client_shared_memory_interface_name,
+                sizeof(clients[i].shm_interface_name));
+        // creat the named smi interface
+
+        // open a shared memory interface.
+        clients[i].shm_fd = -1;
+
+        mode_t oldumask = umask(0);
+        clients[i].shm_fd = shm_open(client_shared_memory_interface_name, O_RDWR | O_CREAT, 0666);
+        if (clients[i].shm_fd == -1) {
+          die("cannot open shared memory \"%s\".", client_shared_memory_interface_name);
+        }
+        (void)umask(oldumask);
+
+        if (ftruncate(clients[i].shm_fd, sizeof(struct shm_structure)) == -1) {
+          die("failed to set size of shared memory \"%s\".", client_shared_memory_interface_name);
+        }
+
+#ifdef CONFIG_FOR_FREEBSD
+        clients[i].shared_memory =
+            (struct shm_structure *)mmap(NULL, sizeof(struct shm_structure), PROT_READ | PROT_WRITE,
+                                         MAP_SHARED, clients[i].shm_fd, 0);
+#endif
+
+#ifdef CONFIG_FOR_LINUX
+        clients[i].shared_memory =
+            (struct shm_structure *)mmap(NULL, sizeof(struct shm_structure), PROT_READ | PROT_WRITE,
+                                         MAP_LOCKED | MAP_SHARED, clients[i].shm_fd, 0);
+#endif
+
+        if (clients[i].shared_memory == (struct shm_structure *)-1) {
+          die("failed to mmap shared memory \"%s\".", client_shared_memory_interface_name);
+        }
+
+        if ((close(clients[i].shm_fd) == -1)) {
+          warn("error closing \"%s\" after mapping.", client_shared_memory_interface_name);
+        }
+
+        // zero it
+        memset(clients[i].shared_memory, 0, sizeof(struct shm_structure));
+        clients[i].shared_memory->version = NQPTP_SHM_STRUCTURES_VERSION;
+
+        /*create mutex attr */
+        err = pthread_mutexattr_init(&shared);
+        if (err != 0) {
+          die("mutex attribute initialization failed - %s.", strerror(errno));
+        }
+        pthread_mutexattr_setpshared(&shared, 1);
+        /*create a mutex */
+        err = pthread_mutex_init((pthread_mutex_t *)&clients[i].shared_memory->shm_mutex, &shared);
+        if (err != 0) {
+          die("mutex initialization failed - %s.", strerror(errno));
+        }
+
+        err = pthread_mutexattr_destroy(&shared);
+        if (err != 0) {
+          die("mutex attribute destruction failed - %s.", strerror(errno));
+        }
+
+        for (i = 0; i < MAX_CLOCKS; i++) {
+          clocks_private[i].client_flags[response] =
+              0; // turn off all client flags in every clock for this client
+        }
+      } else {
+        debug(1, "could not create a client record for client \"%s\".",
+              client_shared_memory_interface_name);
+      }
+    }
+  } else {
+    debug(1, "no client_shared_memory_interface_name");
+  }
+  return response;
+}
+
+int delete_clients() {
+  int response = 0; // okay unless something happens
+  int i;
+  for (i = 0; i < MAX_CLIENTS; i++) {
+    if (clients[i].shm_interface_name[0] != '\0') {
+      if (clients[i].shared_memory != NULL) {
+        // mmap cleanup
+        if (munmap(clients[i].shared_memory, sizeof(struct shm_structure)) != 0) {
+          debug(1, "error unmapping shared memory");
+          response = -1;
+        }
+        // shm_open cleanup
+        if (shm_unlink(clients[i].shm_interface_name) == -1) {
+          debug(1, "error unlinking shared memory \"%s\"", clients[i].shm_interface_name);
+          response = -1;
+        }
+      }
+      clients[i].shm_interface_name[0] = '\0'; // remove the name, just in case
+    }
+  }
+  return response;
+}
 
 int find_clock_source_record(char *sender_string, clock_source_private_data *clocks_private_info) {
   // return the index of the clock in the clock information arrays or -1
@@ -45,7 +187,7 @@ int find_clock_source_record(char *sender_string, clock_source_private_data *clo
   int i = 0;
   int found = 0;
   while ((found == 0) && (i < MAX_CLOCKS)) {
-    if ((clocks_private_info[i].in_use != 0) &&
+    if (((clocks_private_info[i].flags & (1 << clock_is_in_use)) != 0) &&
         (strcasecmp(sender_string, (const char *)&clocks_private_info[i].ip) == 0))
       found = 1;
     else
@@ -58,13 +200,13 @@ int find_clock_source_record(char *sender_string, clock_source_private_data *clo
 
 int create_clock_source_record(char *sender_string,
                                clock_source_private_data *clocks_private_info) {
-   // return the index of a clock entry in the clock information arrays or -1 if full
+  // return the index of a clock entry in the clock information arrays or -1 if full
   // initialise the entries in the shared and private arrays
   int response = -1;
   int i = 0;
   int found = 0; // trying to find an unused entry
   while ((found == 0) && (i < MAX_CLOCKS)) {
-    if (clocks_private_info[i].in_use == 0)
+    if ((clocks_private_info[i].flags & (1 << clock_is_in_use)) == 0)
       found = 1;
     else
       i++;
@@ -89,7 +231,7 @@ int create_clock_source_record(char *sender_string,
 #ifdef MAX_TIMING_SAMPLES
       clocks_private_info[i].vacant_samples = MAX_TIMING_SAMPLES;
 #endif
-      clocks_private_info[i].in_use = 1;
+      clocks_private_info[i].flags |= (1 << clock_is_in_use);
       debug(2, "create record for ip: %s, family: %s.", &clocks_private_info[i].ip,
             clocks_private_info[i].family == AF_INET6 ? "IPv6" : "IPv4");
     } else {
@@ -108,7 +250,7 @@ void manage_clock_sources(uint64_t reception_time, clock_source_private_data *cl
   // do a garbage collect for clock records no longer in use
   for (i = 0; i < MAX_CLOCKS; i++) {
     // only if its in use and not a timing peer... don't need a mutex to check
-    if ((clocks_private_info[i].in_use != 0) &&
+    if (((clocks_private_info[i].flags & (1 << clock_is_in_use)) != 0) &&
         ((clocks_private_info[i].flags & (1 << clock_is_a_timing_peer)) == 0)) {
       int64_t time_since_last_use = reception_time - clocks_private_info[i].time_of_last_use;
       // using a sync timeout to determine when to drop the record...
@@ -124,7 +266,7 @@ void manage_clock_sources(uint64_t reception_time, clock_source_private_data *cl
         debug(2, "delete record for: %s.", &clocks_private_info[i].ip);
         memset(&clocks_private_info[i], 0, sizeof(clock_source_private_data));
         if (old_flags != 0)
-          update_master();
+          update_master(0); // TODO
         else
           debug_log_nqptp_status(2);
       }
@@ -139,7 +281,7 @@ void update_clock_self_identifications(clock_source_private_data *clocks_private
   // first, turn off all the self-id flags
   int i;
   for (i = 0; i < MAX_CLOCKS; i++) {
-    clocks_private_info[i].is_one_of_ours = 0;
+    clocks_private_info[i].flags &= ~(1 << clock_is_one_of_ours);
   }
 
   struct ifaddrs *ifap, *ifa;
@@ -166,11 +308,11 @@ void update_clock_self_identifications(clock_source_private_data *clocks_private
         if (addr != NULL)
           inet_ntop(family, addr, ip_string, sizeof(ip_string));
         if (strlen(ip_string) != 0) {
-          // now set the is_one_of_ours flag of any clock with this ip
+          // now set the clock_is_one_of_ours flag of any clock with this ip
           for (i = 0; i < MAX_CLOCKS; i++) {
             if (strcasecmp(ip_string, clocks_private_info[i].ip) == 0) {
               debug(2, "found an entry for one of our clocks");
-              clocks_private_info[i].is_one_of_ours = 1;
+              clocks_private_info[i].flags |= (1 << clock_is_one_of_ours);
             }
           }
         }
@@ -188,7 +330,7 @@ void debug_log_nqptp_status(int level) {
   int records_in_use = 0;
   int i;
   for (i = 0; i < MAX_CLOCKS; i++)
-    if (clocks_private[i].in_use != 0)
+    if ((clocks_private[i].flags & (1 << clock_is_in_use)) != 0)
       records_in_use++;
   debug(level, "");
   if (records_in_use > 0) {
@@ -200,7 +342,7 @@ void debug_log_nqptp_status(int level) {
     uint32_t non_peer_clock_mask = (1 << clock_is_valid);
     uint32_t non_peer_master_mask = non_peer_clock_mask | (1 << clock_is_master);
     for (i = 0; i < MAX_CLOCKS; i++) {
-      if (clocks_private[i].in_use != 0) {
+      if ((clocks_private[i].flags & (1 << clock_is_in_use)) != 0) {
         if ((clocks_private[i].flags & peer_master_mask) == peer_master_mask) {
           debug(level, "  Peer Master:            %" PRIx64 "  %s.", clocks_private[i].clock_id,
                 clocks_private[i].ip);
@@ -256,7 +398,7 @@ int uint64_cmp(uint64_t a, uint64_t b, const char *cause) {
   }
 }
 
-void update_master() {
+void update_master(int client_id) {
 
   // This implements the IEEE 1588-2008 best master clock algorithm.
 
@@ -353,10 +495,10 @@ void update_master() {
   }
   if (best_so_far == -1) {
     // no master clock
-    //if (old_master != -1) {
-      // but there was a master clock, so remove it
-      debug(1, "Remove master clock.");
-      update_master_clock_info(0, NULL, 0, 0, 0);
+    // if (old_master != -1) {
+    // but there was a master clock, so remove it
+    debug(1, "Remove master clock.");
+    update_master_clock_info(0, NULL, 0, 0, 0);
     //}
     if (timing_peer_count == 0)
       debug(2, "no valid qualified clocks ");
index 85720e7914af68fb36127f50f9250fe779aae0b7..7b666aa81323f5f4e9698397995bd7d87ea58640 100644 (file)
 
 #include "nqptp.h"
 
-// transaction tracking
-enum stage { waiting_for_sync, sync_seen, follow_up_seen };
-
 typedef enum {
+  clock_is_in_use,
+  clock_is_one_of_ours,
   clock_is_valid,
   clock_is_a_timing_peer,
   clock_is_qualified,
@@ -33,15 +32,13 @@ typedef enum {
   clock_is_master
 } clock_flags;
 
-// 8 samples per seconds
-// #define MAX_TIMING_SAMPLES 47
-// #define MAX_TIMING_SAMPLES 1
-
-#ifdef MAX_TIMING_SAMPLES
-typedef struct {
-  uint64_t local_time, clock_time;
-} timing_samples;
-#endif
+/*
+typedef enum {
+  clock_is_a_timing_peer,
+  clock_is_becoming_master,
+  clock_is_master
+} client_flags;
+*/
 
 // information about each clock source
 typedef struct {
@@ -53,8 +50,6 @@ typedef struct {
   uint64_t local_time; // the local time when the offset was calculated
   uint64_t source_time;
   uint64_t local_to_source_time_offset; // add this to the local time to get source time
-  uint32_t flags;
-  uint16_t in_use;
   uint64_t previous_offset, previous_offset_time, last_sync_time;
   uint64_t mastership_start_time; // set to the time of the first sample used as master
 
@@ -63,15 +58,9 @@ typedef struct {
                              // timing peer group
   // (A member of the timing peer group could appear and disappear so will not be gc'ed.)
   // for Announce Qualification
-  uint64_t announce_times[4]; // we'll check qualification and currency using these
-  int is_one_of_ours;         // true if it is one of our own clocks
-
-#ifdef MAX_TIMING_SAMPLES
-  timing_samples samples[MAX_TIMING_SAMPLES];
-  int vacant_samples; // the number of elements in the timing_samples array that are not yet used
-  int next_sample_goes_here; // point to where in the timing samples array the next entries should
-                             // go
-#endif
+  uint64_t announce_times[4];        // we'll check qualification and currency using these
+  uint8_t flags;                     // stuff related specifically to the clock itself
+  uint8_t client_flags[MAX_CLIENTS]; // stuff related to membership of the clients' timing lists
 
   // these are for finding the best clock to use
   // See Figure 27 and 27 pp 89 -- 90 for the Data set comparison algorithm
@@ -87,6 +76,15 @@ typedef struct {
 
 } clock_source_private_data;
 
+// information on each client
+typedef struct {
+  int shm_fd;
+  struct shm_structure *shared_memory; // the client's individual smi interface
+  char shm_interface_name[64];         // it's name
+  int client_id; // the 1-based index number of clocks' client_flags field associated with this
+                 // interface
+} client_record;
+
 int find_clock_source_record(char *sender_string, clock_source_private_data *clocks_private_info);
 
 int create_clock_source_record(char *sender_string, clock_source_private_data *clocks_private_info);
@@ -95,9 +93,15 @@ void update_clock_self_identifications(clock_source_private_data *clocks_private
 
 void manage_clock_sources(uint64_t reception_time, clock_source_private_data *clocks_private_info);
 
+int find_client_id(char *client_shared_memory_interface_name);
+
+int get_client_id(char *client_shared_memory_interface_name);
+
+int delete_clients();
+
 extern clock_source_private_data clocks_private[MAX_CLOCKS];
 
-void update_master();
+void update_master(int client_id);
 
 void debug_log_nqptp_status(int level);
 
index c4589596354c00a66390f414f70f94359e8e7861..a1be6ed65f2141fc18d94423252498407b327366 100644 (file)
@@ -29,50 +29,96 @@ void handle_control_port_messages(char *buf, ssize_t recv_len,
                                   clock_source_private_data *clock_private_info) {
   if (recv_len != -1) {
     buf[recv_len - 1] = 0; // make sure there's a null in it!
-    debug(2, "New timing peer list: \"%s\".", buf);
-    if (buf[0] == 'T') {
-
-      char *ip_list = buf + 1;
-      if (*ip_list == ' ')
-        ip_list++;
-
-      // turn off all is_timing_peer flags
-      int i;
-      for (i = 0; i < MAX_CLOCKS; i++) {
-        clock_private_info[i].flags &=
-            ~(1 << clock_is_a_timing_peer); // turn off peer flag (but not the master flag!)
-        clock_private_info[i].announcements_without_followups = 0; // to allow a possibly silent clocks to be revisited when added to a timing peer list
-        if (strlen(buf) == 1) { // if it's giving an empty timing peer list, that means drop mastership from the past
-          clock_private_info[i].flags &= ~(1 << clock_is_master);
-          clock_private_info[i].mastership_start_time = 0;
-          clock_private_info[i].previous_offset_time = 0;
-        }
-      }
-
-      while (ip_list != NULL) {
-        char *new_ip = strsep(&ip_list, " ");
-        // look for the IP in the list of clocks, and create an inert entry if not there
-        if ((new_ip != NULL) && (new_ip[0] != 0)) {
-          int t = find_clock_source_record(new_ip, clock_private_info);
-          if (t == -1)
-            t = create_clock_source_record(new_ip, clock_private_info);
-          if (t != -1) // if the clock table is not full, show it's a timing peer
-            clock_private_info[t].flags |= (1 << clock_is_a_timing_peer);
-          // otherwise, drop it
+    debug(1, "New control port message: \"%s\".", buf);
+    // we need to get the client shared memory interface name from the front
+    char *ip_list = buf;
+    char *smi_name = strsep(&ip_list, " ");
+    char *command = NULL;
+    if (smi_name != NULL) {
+      debug(1, "SMI Name: \"%s\"", smi_name);
+      int client_id = 0;
+      if (ip_list != NULL)
+        command = strsep(&ip_list, " ");
+      if ((command == NULL) || ((strcmp(command, "T") == 0) && (ip_list == NULL))) {
+        // clear all the flags, but only if the client exists
+        client_id = find_client_id(smi_name); // don't create a record
+        if (client_id != -1) {
+          // turn off all is_timing_peer flags
+          int i;
+          for (i = 0; i < MAX_CLOCKS; i++) {
+            // e.g. (obsolete)
+            clock_private_info[i].flags &= ~(1 << clock_is_master);
+            clock_private_info[i].mastership_start_time = 0;
+            clock_private_info[i].previous_offset_time = 0;
+
+            // if a clock would now stop being a master everywhere
+            // it should drop mastership history and do a sync when it becomes master again
+            if ((clock_private_info[i].client_flags[client_id] & (1 << clock_is_master)) !=
+                0) { // if clock[i] is master for this client's timing group
+              int c;
+              int this_clock_is_master_elsewhere = 0;
+              for (c = 0; c < MAX_CLIENTS; c++) {
+                if ((c != client_id) &&
+                    ((clock_private_info[i].client_flags[c] & (1 << clock_is_master)) != 0))
+                  this_clock_is_master_elsewhere = 1;
+              }
+              if (this_clock_is_master_elsewhere == 0) {
+                clock_private_info[i].mastership_start_time = 0;
+                clock_private_info[i].previous_offset_time = 0;
+              }
+            }
+            clock_private_info[i].client_flags[client_id] = 0;
+          }
         }
-      }
+      } else {
+        client_id = get_client_id(smi_name); // create the record if it doesn't exist
+        if (client_id != -1) {
+          if (strcmp(command, "T") == 0) {
+            // turn off all is_timing_peer flags
+            int i;
+            for (i = 0; i < MAX_CLOCKS; i++) {
+              clock_private_info[i].flags &=
+                  ~(1 << clock_is_a_timing_peer); // turn off peer flag (but not the master flag!)
+              clock_private_info[i].client_flags[client_id] &=
+                  ~(1 << clock_is_a_timing_peer); // turn off peer flag (but not the master flag!)
+              clock_private_info[i].announcements_without_followups =
+                  0; // to allow a possibly silent clock to be revisited when added to a timing
+                     // peer list
+            }
+            while (ip_list != NULL) {
+              char *new_ip = strsep(&ip_list, " ");
+              // look for the IP in the list of clocks, and create an inert entry if not there
+              if ((new_ip != NULL) && (new_ip[0] != 0)) {
+                int t = find_clock_source_record(new_ip, clock_private_info);
+                if (t == -1)
+                  t = create_clock_source_record(new_ip, clock_private_info);
+                if (t != -1) { // if the clock table is not full, show it's a timing peer
+                  clock_private_info[t].flags |= (1 << clock_is_a_timing_peer);
+                  clock_private_info[t].client_flags[client_id] |= (1 << clock_is_a_timing_peer);
+                }
+                // otherwise, drop it
+              }
+            }
 
-      // now find and mark the best clock in the timing peer list as the master
-      update_master();
+            // now find and mark the best clock in the timing peer list as the master
+            update_master(client_id);
 
-      debug(2, "Timing group start");
-      for (i = 0; i < MAX_CLOCKS; i++) {
-        if ((clock_private_info[i].flags & (1 << clock_is_a_timing_peer)) != 0)
-          debug(2, "%s.", &clock_private_info[i].ip);
+            debug(2, "Timing group start");
+            for (i = 0; i < MAX_CLOCKS; i++) {
+              if ((clock_private_info[i].client_flags[client_id] & (1 << clock_is_a_timing_peer)) !=
+                  0)
+                debug(2, "%s.", &clock_private_info[i].ip);
+            }
+            debug(2, "Timing group end");
+          } else {
+            warn("Unrecognised string on the control port.");
+          }
+        } else {
+          warn("Could not find or create a record for SMI Interface \"%s\".", smi_name);
+        }
       }
-      debug(2, "Timing group end");
     } else {
-      warn("Unrecognised string on the control port.");
+      warn("SMI Interface Name not found on the control port.");
     }
   } else {
     warn("Bad packet on the control port.");
@@ -81,8 +127,8 @@ void handle_control_port_messages(char *buf, ssize_t recv_len,
 
 void handle_announce(char *buf, ssize_t recv_len, clock_source_private_data *clock_private_info,
                      uint64_t reception_time) {
-  // reject Announce messages from self
-  if (clock_private_info->is_one_of_ours == 0) {
+  // only process Announce messages that do not come from self
+  if ((clock_private_info->flags & (1 << clock_is_one_of_ours)) == 0) {
     // debug_print_buffer(1, buf, (size_t) recv_len);
     // make way for the new time
     if ((size_t)recv_len >= sizeof(struct ptp_announce_message)) {
@@ -220,7 +266,7 @@ void handle_announce(char *buf, ssize_t recv_len, clock_source_private_data *clo
             clock_private_info->flags &= ~(1 << clock_is_qualified);
           else
             clock_private_info->flags |= (1 << clock_is_qualified);
-          update_master();
+          update_master(0); // TODO -- use client_id here
         }
       } else {
         if ((clock_private_info->flags & (1 << clock_is_qualified)) !=
@@ -285,7 +331,6 @@ void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len,
     time_since_previous_offset = reception_time - clock_private_info->previous_offset_time;
   }
 
-
   if ((clock_private_info->flags & (1 << clock_is_becoming_master)) != 0) {
     // we definitely have at least one sample since the request was made to
     // designate it a master, so we assume it is legitimate. That is, we assume
@@ -377,7 +422,8 @@ void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len,
     clock_private_info->flags |= 1 << clock_is_master;
     clock_private_info->previous_offset_time = 0;
     debug_log_nqptp_status(2);
-  } else if ((clock_private_info->previous_offset_time != 0) && (time_since_previous_offset < 300000000000)) {
+  } else if ((clock_private_info->previous_offset_time != 0) &&
+             (time_since_previous_offset < 300000000000)) {
     // i.e. if it's not becoming a master and there has been a previous follow_up
     int64_t time_since_last_sync = reception_time - clock_private_info->last_sync_time;
     int64_t sync_timeout = 300000000000; // nanoseconds
@@ -407,12 +453,12 @@ void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len,
         // we take any positive or a limited negative jitter as a sync event
         if (jitter < 0) {
           if (clock_private_info->follow_up_number <
-                 (5 * 8)) // at the beginning (8 samples per second)
+              (5 * 8)) // at the beginning (8 samples per second)
             offset = clock_private_info->previous_offset + jitter / 16;
           else
             offset = clock_private_info->previous_offset + jitter / 64;
         } else if (clock_private_info->follow_up_number <
-                 (5 * 8)) // at the beginning (8 samples per second)
+                   (5 * 8)) // at the beginning (8 samples per second)
           offset =
               clock_private_info->previous_offset + jitter / 1; // accept positive changes quickly
         else
@@ -427,14 +473,17 @@ void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len,
               clock_private_info->ip);
       // leave the offset as it was coming in and take it as a sync time
       clock_private_info->last_sync_time = reception_time;
-      clock_private_info->mastership_start_time = reception_time; // mastership is reset to this time...
+      clock_private_info->mastership_start_time =
+          reception_time; // mastership is reset to this time...
       clock_private_info->previous_offset_time = 0;
     }
   } else {
     clock_private_info->last_sync_time = reception_time;
     if (time_since_previous_offset >= 300000000000) {
-      debug(1,"Long interval: %f seconds since previous follow_up", time_since_previous_offset * 1E-9);
-      clock_private_info->mastership_start_time = reception_time; // mastership is reset to this time...
+      debug(1, "Long interval: %f seconds since previous follow_up",
+            time_since_previous_offset * 1E-9);
+      clock_private_info->mastership_start_time =
+          reception_time; // mastership is reset to this time...
       clock_private_info->previous_offset_time = 0;
     }
   }
@@ -454,6 +503,6 @@ void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len,
           clock_private_info->ip);
     clock_private_info->flags |=
         (1 << clock_is_valid); // valid because it has at least one follow_up
-    update_master();
+    update_master(0);          // TODO
   }
 }
index fee940382b8721a1689b0a91b491a53709966092..c89b359bc51e284f55471d4c43b02b6ceab74de1 100644 (file)
@@ -20,9 +20,9 @@
 #ifndef NQPTP_MESSAGE_HANDLERS_H
 #define NQPTP_MESSAGE_HANDLERS_H
 
-#include <sys/types.h>
 #include "general-utilities.h"
 #include "nqptp-clock-sources.h"
+#include <sys/types.h>
 
 void handle_announce(char *buf, ssize_t recv_len, clock_source_private_data *clock_private_info,
                      uint64_t reception_time);
index f5edbd48c0a2053fd4c8a9268c8216f760339ac0..7f2b2b8ac8057094e9add9b51e3ab8d2d96b96bd 100644 (file)
 #ifndef NQPTP_SHM_STRUCTURES_H
 #define NQPTP_SHM_STRUCTURES_H
 
-#define STORAGE_ID "/nqptp"
-#define MAX_CLOCKS 64
-#define NQPTP_SHM_STRUCTURES_VERSION 6
+#define NQPTP_SHM_STRUCTURES_VERSION 7
 #define NQPTP_CONTROL_PORT 9000
 
-// the control port will accept a UDP packet with the first letter being:
-// "T", followed by a space and then a space-delimited
-// list of ip numbers, either IPv4 or IPv6
+// The control port will accept a UDP packet with the first letter being
+// "T", followed by the name of the shared memory interface, which should be of
+// the form nqptp-<up-to-12-hex-digits>. This can be followed by nothing or by
+// a space and then a space-delimited list of ip numbers, either IPv4 or IPv6
 // the whole not to exceed 4096 characters in total
-// The IPs will become the new list of timing peers, replacing any previous
+// The IPs, if provided, will become the new list of timing peers, clearing or replacing any
+// previous list. If the master clock of the new list is the same as that of the old list, it is
+// retained without having to resynchronise. This means that non-master devices can be added and
+// removed without disturbing the existing-and-continuing master clock.
 
 #include <inttypes.h>
 #include <pthread.h>
@@ -37,7 +39,6 @@
 struct shm_structure {
   pthread_mutex_t shm_mutex;            // for safely accessing the structure
   uint16_t version;                     // check this is equal to NQPTP_SHM_STRUCTURES_VERSION
-  uint32_t flags;                       // unused
   uint64_t master_clock_id;             // the current master clock
   char master_clock_ip[64];             // where it's coming from
   uint64_t local_time;                  // the time when the offset was calculated
diff --git a/nqptp.c b/nqptp.c
index 062520bb33d801635395b03bbca0c12112ba495a..e58280c924fe53c4dd829ea058a8c476d657d48a 100644 (file)
--- a/nqptp.c
+++ b/nqptp.c
@@ -63,8 +63,6 @@
 
 sockets_open_bundle sockets_open_stuff;
 
-int master_clock_index = -1;
-
 typedef struct {
   uint64_t trigger_time;
   uint64_t (*task)(uint64_t nominal_call_time, void *private_data);
@@ -90,7 +88,7 @@ int epoll_fd;
 void update_master_clock_info(uint64_t master_clock_id, const char *ip, uint64_t local_time,
                               uint64_t local_to_master_offset, uint64_t mastership_start_time) {
 
-  //debug(1,"update_master_clock_info start");
+  // debug(1,"update_master_clock_info start");
   if (shared_memory->master_clock_id != master_clock_id)
     debug_log_nqptp_status(1);
   int rc = pthread_mutex_lock(&shared_memory->shm_mutex);
@@ -112,7 +110,7 @@ void update_master_clock_info(uint64_t master_clock_id, const char *ip, uint64_t
   rc = pthread_mutex_unlock(&shared_memory->shm_mutex);
   if (rc != 0)
     warn("Can't release mutex after updating master clock!");
-  //debug(1,"update_master_clock_info done");
+  // debug(1,"update_master_clock_info done");
 }
 
 void goodbye(void) {
@@ -120,6 +118,8 @@ void goodbye(void) {
   unsigned int i;
   for (i = 0; i < sockets_open_stuff.sockets_open; i++)
     close(sockets_open_stuff.sockets[i].number);
+  // close off shared memory interfaces
+
   if (shared_memory != NULL) {
     // mmap cleanup
     if (munmap(shared_memory, sizeof(struct shm_structure)) != 0)
@@ -128,6 +128,9 @@ void goodbye(void) {
     if (shm_unlink(STORAGE_ID) == -1)
       debug(1, "error unlinking shared memory \"%s\"", STORAGE_ID);
   }
+
+  delete_clients();
+
   if (epoll_fd != -1)
     close(epoll_fd);
 
@@ -431,7 +434,7 @@ uint64_t broadcasting_task(uint64_t call_time, __attribute__((unused)) void *pri
   int i;
   for (i = 0; i < MAX_CLOCKS; i++) {
     if ((clocks_private[i].announcements_without_followups == 3) &&
-        (clocks_private[i].is_one_of_ours == 0)) {
+        ((clocks_private[i].flags & (1 << clock_is_one_of_ours)) == 0)) {
       debug(1, "Found a silent clock %" PRIx64 " at %s.", clocks_private[i].clock_id,
             clocks_private[i].ip);
       // send an Announce message to attempt to waken this silent PTP clock by
@@ -532,12 +535,12 @@ uint64_t broadcasting_task(uint64_t call_time, __attribute__((unused)) void *pri
     }
   }
 
-/*
-  uint64_t announce_interval = 1;
-  announce_interval = announce_interval << (8 + aPTPinitialLogAnnounceInterval);
-  announce_interval = announce_interval * 1000000000;
-  announce_interval = announce_interval >> 8; // nanoseconds
-  return call_time + announce_interval;
-*/
+  /*
+    uint64_t announce_interval = 1;
+    announce_interval = announce_interval << (8 + aPTPinitialLogAnnounceInterval);
+    announce_interval = announce_interval * 1000000000;
+    announce_interval = announce_interval >> 8; // nanoseconds
+    return call_time + announce_interval;
+  */
   return call_time + 50000000;
 }
diff --git a/nqptp.h b/nqptp.h
index 327aba921add22eaf95cbdaf35125fda5fa93a8a..f86c968933eb167f9069e7b699fe043eee13a8a9 100644 (file)
--- a/nqptp.h
+++ b/nqptp.h
 
 #include "nqptp-shm-structures.h"
 
+#define STORAGE_ID "/nqptp"
+#define MAX_CLOCKS 64
+#define MAX_CLIENTS 16
 #define MAX_OPEN_SOCKETS 16
 
 // When a new timing peer group is created, one of the clocks in the
-// group becomes the master and its native time becomes the "master time".
+// group may become the master and its native time becomes the "master time".
 // This is what is provided to the client.
 
-extern int master_clock_index;
-extern struct shm_structure *shared_memory;
+// An NQPTP client interface communicates through a shared memory interface named by the
+// shm_interface_name It provides the shm_interface_name at the start of every control message it
+// sends through port 9000. Following the name, the client can specify the members -- the "PTP
+// Instances" -- of a "PTP Network" it wishes to monitor. This is a "timing group" in AirPlay 2
+// parlance, it seems.
 
 void update_master_clock_info(uint64_t master_clock_id, const char *ip, uint64_t local_time,
                               uint64_t local_to_master_offset, uint64_t mastership_start_time);