├── .gitignore ├── CODING_STYLE.org ├── COPYING ├── README.org ├── address.h ├── bmc.c ├── bmc.h ├── clock.c ├── clock.h ├── clockadj.c ├── clockadj.h ├── clockcheck.c ├── clockcheck.h ├── config.c ├── config.h ├── contain.h ├── ddt.h ├── default.cfg ├── dm.h ├── ds.h ├── ether.h ├── fault.c ├── fault.h ├── fd.h ├── filter.c ├── filter.h ├── filter_private.h ├── foreign.h ├── fsm.c ├── fsm.h ├── gPTP.cfg ├── hwstamp_ctl.8 ├── hwstamp_ctl.c ├── incdefs.sh ├── libmain.c ├── libmain.h ├── linreg.c ├── linreg.h ├── makefile ├── mave.c ├── mave.h ├── missing.h ├── mmedian.c ├── mmedian.h ├── msg.c ├── msg.h ├── notification.h ├── pdt.h ├── phc.c ├── phc.h ├── phc2sys.8 ├── phc2sys.c ├── pi.c ├── pi.h ├── pmc.8 ├── pmc.c ├── pmc_common.c ├── pmc_common.h ├── port.c ├── port.h ├── print.c ├── print.h ├── ptp4l.8 ├── ptp4l.c ├── ptp4l_asi.c ├── raw.c ├── raw.h ├── servo.c ├── servo.h ├── servo_private.h ├── sk.c ├── sk.h ├── stats.c ├── stats.h ├── sysoff.c ├── sysoff.h ├── tlv.c ├── tlv.h ├── tmv.h ├── transport.c ├── transport.h ├── transport_private.h ├── udp.c ├── udp.h ├── udp6.c ├── udp6.h ├── uds.c ├── uds.h ├── util.c ├── util.h ├── version.c ├── version.h └── version.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /*.d 2 | /*.o 3 | /hwstamp_ctl 4 | /phc2sys 5 | /pmc 6 | /ptp4l 7 | -------------------------------------------------------------------------------- /CODING_STYLE.org: -------------------------------------------------------------------------------- 1 | 2 | * General 3 | 4 | For this project, we are using the Linux kernel coding style, with a 5 | bit of latitude, as described below. The kernel style can be quickly 6 | summarized like this: 7 | 8 | - text width is 80 columns 9 | - indentation by tabs 10 | - tab width is 8 spaces 11 | - identifiers are lower case with underscores 12 | - macros are upper case 13 | - braces and spacing follow K&R style 14 | 15 | * Using checkpatch.pl 16 | 17 | You can use the Linux kernel's checkpatch.pl script to sanity check 18 | your work. Sometimes that script warns about code which is in fact 19 | fine, so use your good taste. I use the following code as my 20 | pre-commit hook. 21 | 22 | #+BEGIN_EXAMPLE 23 | if git rev-parse --verify HEAD >/dev/null 2>&1 24 | then 25 | against=HEAD 26 | else 27 | # Initial commit: diff against an empty tree object 28 | against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 29 | fi 30 | 31 | git diff --cached $against -- | ../linux/scripts/checkpatch.pl \ 32 | --ignore BRACES \ 33 | --ignore PREFER_PR_LEVEL \ 34 | --no-signoff \ 35 | - 36 | #+END_EXAMPLE 37 | 38 | * Exceptions 39 | 40 | ** Use of lowerCamelCase and UpperCamelCase 41 | 42 | The messages and data sets in the PTP and gPTP standards are full 43 | of stuff that looks a bit like C-language pseudo code, and all this 44 | is unfortunately in camel case. In those cases where the linuxptp 45 | code is closely related to these fields, we retain the camel case, 46 | even though it hurts our eyes to look at. 47 | 48 | The alternative would have been to convert the field names into 49 | lower case underscore, but that would have over extended the 50 | already ridiculously long names, such as logMinPdelayReqInterval 51 | and observedParentOffsetScaledLogVariance. 52 | 53 | The exception permitting CamelCase applies primarily to the 54 | declarations found in the following header files. 55 | 56 | - ddt.h 57 | - ds.h 58 | - msg.h 59 | - pdt.h 60 | - tlv.h 61 | 62 | ** Braces around single if-else bodies 63 | 64 | In the Linux kernel style, if-else bodies containing just a single 65 | line are not placed within curly braces. Therefore you will often 66 | see code like the following example. 67 | 68 | #+BEGIN_EXAMPLE 69 | if (!x) 70 | do_zero(); 71 | else 72 | do_nonzero(); 73 | #+END_EXAMPLE 74 | 75 | In this situation we still like to see the braces, the rationale 76 | being that the if-else bodies tend to accumulate new statements 77 | over time, especially on the error path. Using the strict kernel 78 | style thus results in patches (and annotated views) that show a 79 | bogus change in the test expression, and this requires extra mental 80 | effort when reviewing patches, just to realize that no logical 81 | change has occurred. Additionally, having the braces hardly uses up 82 | more vertical space than not having them, so we generally include 83 | them as shown in the following example. 84 | 85 | #+BEGIN_EXAMPLE 86 | if (!x) { 87 | do_zero(); 88 | } else { 89 | do_nonzero(); 90 | } 91 | #+END_EXAMPLE 92 | 93 | ** Line lengths 94 | 95 | We try to keep the line length to 80 characters wide. However, 96 | sometimes it happens that, no matter how hard we try, we find 97 | ourselves going a bit over that limit. This is especially true in 98 | connection with the long CamelCase identifiers mentioned above. 99 | Breaking a statement over two lines can look even worse than having 100 | one line too long, so please exercise judgement when applying the 101 | line length rule. 102 | -------------------------------------------------------------------------------- /address.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file address.h 3 | * @brief Definition of a structure to hold an address. 4 | * @note Copyright (C) 2014 Red Hat, Inc., Jiri Benc 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_ADDRESS_H 21 | #define HAVE_ADDRESS_H 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | struct address { 28 | socklen_t len; 29 | union { 30 | struct sockaddr_storage ss; 31 | struct sockaddr_in sin; 32 | struct sockaddr_in6 sin6; 33 | struct sockaddr_un sun; 34 | struct sockaddr sa; 35 | }; 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /bmc.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file bmc.c 3 | * @note Copyright (C) 2011 Richard Cochran 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include 20 | 21 | #include "bmc.h" 22 | #include "ds.h" 23 | 24 | #define A_BETTER_TOPO 2 25 | #define A_BETTER 1 26 | #define B_BETTER -1 27 | #define B_BETTER_TOPO -2 28 | 29 | static int dscmp2(struct dataset *a, struct dataset *b) 30 | { 31 | int diff; 32 | unsigned int A = a->stepsRemoved, B = b->stepsRemoved; 33 | 34 | if (A + 1 < B) 35 | return A_BETTER; 36 | if (B + 1 < A) 37 | return B_BETTER; 38 | /* 39 | * We ignore the "error-1" conditions mentioned in the 40 | * standard, since there is nothing we can do about it anyway. 41 | */ 42 | if (A < B) { 43 | diff = memcmp(&b->receiver, &b->sender, sizeof(b->receiver)); 44 | if (diff < 0) 45 | return A_BETTER; 46 | if (diff > 0) 47 | return A_BETTER_TOPO; 48 | /* error-1 */ 49 | return 0; 50 | } 51 | if (A > B) { 52 | diff = memcmp(&a->receiver, &a->sender, sizeof(a->receiver)); 53 | if (diff < 0) 54 | return B_BETTER; 55 | if (diff > 0) 56 | return B_BETTER_TOPO; 57 | /* error-1 */ 58 | return 0; 59 | } 60 | 61 | diff = memcmp(&a->sender, &b->sender, sizeof(a->sender)); 62 | if (diff < 0) 63 | return A_BETTER_TOPO; 64 | if (diff > 0) 65 | return B_BETTER_TOPO; 66 | 67 | if (a->receiver.portNumber < b->receiver.portNumber) 68 | return A_BETTER_TOPO; 69 | if (a->receiver.portNumber > b->receiver.portNumber) 70 | return B_BETTER_TOPO; 71 | /* 72 | * If we got this far, it means "error-2" has occured. 73 | */ 74 | return 0; 75 | } 76 | 77 | int dscmp(struct dataset *a, struct dataset *b) 78 | { 79 | int diff; 80 | 81 | if (a == b) 82 | return 0; 83 | if (a && !b) 84 | return A_BETTER; 85 | if (b && !a) 86 | return B_BETTER; 87 | 88 | diff = memcmp(&a->identity, &b->identity, sizeof(a->identity)); 89 | 90 | if (!diff) 91 | return dscmp2(a, b); 92 | 93 | if (a->priority1 < b->priority1) 94 | return A_BETTER; 95 | if (a->priority1 > b->priority1) 96 | return B_BETTER; 97 | 98 | if (a->quality.clockClass < b->quality.clockClass) 99 | return A_BETTER; 100 | if (a->quality.clockClass > b->quality.clockClass) 101 | return B_BETTER; 102 | 103 | if (a->quality.clockAccuracy < b->quality.clockAccuracy) 104 | return A_BETTER; 105 | if (a->quality.clockAccuracy > b->quality.clockAccuracy) 106 | return B_BETTER; 107 | 108 | if (a->quality.offsetScaledLogVariance < 109 | b->quality.offsetScaledLogVariance) 110 | return A_BETTER; 111 | if (a->quality.offsetScaledLogVariance > 112 | b->quality.offsetScaledLogVariance) 113 | return B_BETTER; 114 | 115 | if (a->priority2 < b->priority2) 116 | return A_BETTER; 117 | if (a->priority2 > b->priority2) 118 | return B_BETTER; 119 | 120 | return diff < 0 ? A_BETTER : B_BETTER; 121 | } 122 | 123 | enum port_state bmc_state_decision(struct clock *c, struct port *r) 124 | { 125 | struct dataset *clock_ds, *clock_best, *port_best; 126 | enum port_state ps; 127 | 128 | clock_ds = clock_default_ds(c); 129 | clock_best = clock_best_foreign(c); 130 | port_best = port_best_foreign(r); 131 | ps = port_state(r); 132 | 133 | if (!port_best && PS_LISTENING == ps) 134 | return ps; 135 | 136 | if (clock_class(c) <= 127) { 137 | if (dscmp(clock_ds, port_best) > 0) { 138 | return PS_GRAND_MASTER; /*M1*/ 139 | } else { 140 | return PS_PASSIVE; /*P1*/ 141 | } 142 | } 143 | 144 | if (dscmp(clock_ds, clock_best) > 0) { 145 | return PS_GRAND_MASTER; /*M2*/ 146 | } 147 | 148 | if (clock_best_port(c) == r) { 149 | return PS_SLAVE; /*S1*/ 150 | } 151 | 152 | if (dscmp(clock_best, port_best) == A_BETTER_TOPO) { 153 | return PS_PASSIVE; /*P2*/ 154 | } else { 155 | return PS_MASTER; /*M3*/ 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /bmc.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file bmc.h 3 | * @brief Best master clock algorithm 4 | * @note Copyright (C) 2011 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_BMC_H 21 | #define HAVE_BMC_H 22 | 23 | #include "clock.h" 24 | #include "port.h" 25 | #include "fsm.h" 26 | 27 | /** 28 | * BMC state decision algorithm. 29 | * @param c The local clock. 30 | * @param r The port in question. 31 | * @return A @ref port_state value as the recommended state. 32 | */ 33 | enum port_state bmc_state_decision(struct clock *c, struct port *r); 34 | 35 | /** 36 | * Compare two data sets. 37 | * @param a A dataset to compare. 38 | * @param b A dataset to compare. 39 | * @return An integer less than, equal to, or greater than zero 40 | * if the dataset @a a is found, respectively, to be 41 | * less than, to match, or be greater than @a b. 42 | */ 43 | int dscmp(struct dataset *a, struct dataset *b); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /clockadj.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file clockadj.c 3 | * @note Copyright (C) 2013 Richard Cochran 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "clockadj.h" 25 | #include "missing.h" 26 | #include "print.h" 27 | 28 | #define NS_PER_SEC 1000000000LL 29 | 30 | static int realtime_leap_bit; 31 | static long realtime_hz; 32 | static long realtime_nominal_tick; 33 | 34 | void clockadj_init(clockid_t clkid) 35 | { 36 | #ifdef _SC_CLK_TCK 37 | if (clkid == CLOCK_REALTIME) { 38 | /* This is USER_HZ in the kernel. */ 39 | realtime_hz = sysconf(_SC_CLK_TCK); 40 | if (realtime_hz > 0) { 41 | /* This is TICK_USEC in the kernel. */ 42 | realtime_nominal_tick = 43 | (1000000 + realtime_hz / 2) / realtime_hz; 44 | } 45 | } 46 | #endif 47 | } 48 | 49 | void clockadj_set_freq(clockid_t clkid, double freq) 50 | { 51 | struct timex tx; 52 | memset(&tx, 0, sizeof(tx)); 53 | 54 | /* With system clock set also the tick length. */ 55 | if (clkid == CLOCK_REALTIME && realtime_nominal_tick) { 56 | tx.modes |= ADJ_TICK; 57 | tx.tick = round(freq / 1e3 / realtime_hz) + realtime_nominal_tick; 58 | freq -= 1e3 * realtime_hz * (tx.tick - realtime_nominal_tick); 59 | } 60 | 61 | tx.modes |= ADJ_FREQUENCY; 62 | tx.freq = (long) (freq * 65.536); 63 | if (clock_adjtime(clkid, &tx) < 0) 64 | pr_err("failed to adjust the clock: %m"); 65 | } 66 | 67 | double clockadj_get_freq(clockid_t clkid) 68 | { 69 | double f = 0.0; 70 | struct timex tx; 71 | memset(&tx, 0, sizeof(tx)); 72 | if (clock_adjtime(clkid, &tx) < 0) { 73 | pr_err("failed to read out the clock frequency adjustment: %m"); 74 | } else { 75 | f = tx.freq / 65.536; 76 | if (clkid == CLOCK_REALTIME && realtime_nominal_tick) 77 | f += 1e3 * realtime_hz * (tx.tick - realtime_nominal_tick); 78 | } 79 | return f; 80 | } 81 | 82 | void clockadj_step(clockid_t clkid, int64_t step) 83 | { 84 | struct timex tx; 85 | int sign = 1; 86 | if (step < 0) { 87 | sign = -1; 88 | step *= -1; 89 | } 90 | memset(&tx, 0, sizeof(tx)); 91 | tx.modes = ADJ_SETOFFSET | ADJ_NANO; 92 | tx.time.tv_sec = sign * (step / NS_PER_SEC); 93 | tx.time.tv_usec = sign * (step % NS_PER_SEC); 94 | /* 95 | * The value of a timeval is the sum of its fields, but the 96 | * field tv_usec must always be non-negative. 97 | */ 98 | if (tx.time.tv_usec < 0) { 99 | tx.time.tv_sec -= 1; 100 | tx.time.tv_usec += 1000000000; 101 | } 102 | if (clock_adjtime(clkid, &tx) < 0) 103 | pr_err("failed to step clock: %m"); 104 | } 105 | 106 | void sysclk_set_leap(int leap) 107 | { 108 | clockid_t clkid = CLOCK_REALTIME; 109 | struct timex tx; 110 | const char *m = NULL; 111 | memset(&tx, 0, sizeof(tx)); 112 | tx.modes = ADJ_STATUS; 113 | switch (leap) { 114 | case -1: 115 | tx.status = STA_DEL; 116 | m = "clock set to delete leap second at midnight (UTC)"; 117 | break; 118 | case 1: 119 | tx.status = STA_INS; 120 | m = "clock set to insert leap second at midnight (UTC)"; 121 | break; 122 | default: 123 | tx.status = 0; 124 | } 125 | if (clock_adjtime(clkid, &tx) < 0) 126 | pr_err("failed to set the clock status: %m"); 127 | else if (m) 128 | pr_notice("%s", m); 129 | realtime_leap_bit = tx.status; 130 | } 131 | 132 | int sysclk_max_freq(void) 133 | { 134 | clockid_t clkid = CLOCK_REALTIME; 135 | int f = 0; 136 | struct timex tx; 137 | memset(&tx, 0, sizeof(tx)); 138 | if (clock_adjtime(clkid, &tx) < 0) 139 | pr_err("failed to read out the clock maximum adjustment: %m"); 140 | else 141 | f = tx.tolerance / 65.536; 142 | if (!f) 143 | f = 500000; 144 | 145 | /* The kernel allows the tick length to be adjusted up to 10%. But use 146 | it only if the overall frequency of the clock can be adjusted 147 | continuously with the tick and freq fields (i.e. hz <= 1000). */ 148 | if (realtime_nominal_tick && 2 * f >= 1000 * realtime_hz) 149 | f = realtime_nominal_tick / 10 * 1000 * realtime_hz; 150 | 151 | return f; 152 | } 153 | 154 | void sysclk_set_sync(void) 155 | { 156 | clockid_t clkid = CLOCK_REALTIME; 157 | struct timex tx; 158 | memset(&tx, 0, sizeof(tx)); 159 | /* Clear the STA_UNSYNC flag from the status and keep the maxerror 160 | value (which is increased automatically by 500 ppm) below 16 seconds 161 | to avoid getting the STA_UNSYNC flag back. */ 162 | tx.modes = ADJ_STATUS | ADJ_MAXERROR; 163 | tx.status = realtime_leap_bit; 164 | if (clock_adjtime(clkid, &tx) < 0) 165 | pr_err("failed to set clock status and maximum error: %m"); 166 | } 167 | -------------------------------------------------------------------------------- /clockadj.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file clockadj.h 3 | * @brief Wraps clock_adjtime functionality. 4 | * @note Copyright (C) 2013 Miroslav Lichvar 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_CLOCKADJ_H 21 | #define HAVE_CLOCKADJ_H 22 | 23 | #include 24 | #include 25 | 26 | /** 27 | * Initialize state needed when adjusting or reading the clock. 28 | * @param clkid A clock ID obtained using phc_open() or CLOCK_REALTIME. 29 | */ 30 | void clockadj_init(clockid_t clkid); 31 | 32 | /** 33 | * Set clock's frequency offset. 34 | * @param clkid A clock ID obtained using phc_open() or CLOCK_REALTIME. 35 | * @param freq The frequency offset in parts per billion (ppb). 36 | */ 37 | void clockadj_set_freq(clockid_t clkid, double freq); 38 | 39 | /** 40 | * Read clock's frequency offset. 41 | * @param clkid A clock ID obtained using phc_open() or CLOCK_REALTIME. 42 | * @return The frequency offset in parts per billion (ppb). 43 | */ 44 | double clockadj_get_freq(clockid_t clkid); 45 | 46 | /** 47 | * Step clock's time. 48 | * @param clkid A clock ID obtained using phc_open() or CLOCK_REALTIME. 49 | * @param step The time step in nanoseconds. 50 | */ 51 | void clockadj_step(clockid_t clkid, int64_t step); 52 | 53 | /** 54 | * Set the system clock to insert/delete leap second at midnight. 55 | * @param leap +1 to insert leap second, -1 to delete leap second, 56 | * 0 to reset the leap state. 57 | */ 58 | void sysclk_set_leap(int leap); 59 | 60 | /** 61 | * Read maximum frequency adjustment of the system clock (CLOCK_REALTIME). 62 | * @return The maximum frequency adjustment in parts per billion (ppb). 63 | */ 64 | int sysclk_max_freq(void); 65 | 66 | /** 67 | * Mark the system clock as synchronized to let the kernel synchronize 68 | * the real-time clock (RTC) to it. 69 | */ 70 | void sysclk_set_sync(void); 71 | #endif 72 | -------------------------------------------------------------------------------- /clockcheck.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file clockcheck.c 3 | * @note Copyright (C) 2013 Miroslav Lichvar 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | #include "clockcheck.h" 24 | #include "print.h" 25 | 26 | #define CHECK_MIN_INTERVAL 100000000 27 | #define CHECK_MAX_FREQ 900000000 28 | 29 | struct clockcheck { 30 | /* Sanity frequency limit */ 31 | int freq_limit; 32 | /* Frequency was set at least once */ 33 | int freq_known; 34 | /* Current frequency */ 35 | int current_freq; 36 | /* Maximum and minimum frequency since last update */ 37 | int max_freq; 38 | int min_freq; 39 | uint64_t last_ts; 40 | uint64_t last_mono_ts; 41 | }; 42 | 43 | struct clockcheck *clockcheck_create(int freq_limit) 44 | { 45 | struct clockcheck *cc; 46 | cc = calloc(1, sizeof(*cc)); 47 | if (!cc) 48 | return NULL; 49 | cc->freq_limit = freq_limit; 50 | cc->max_freq = -CHECK_MAX_FREQ; 51 | cc->min_freq = CHECK_MAX_FREQ; 52 | return cc; 53 | } 54 | 55 | int clockcheck_sample(struct clockcheck *cc, uint64_t ts) 56 | { 57 | uint64_t mono_ts; 58 | int64_t interval, mono_interval; 59 | double max_foffset, min_foffset; 60 | struct timespec now; 61 | int ret = 0; 62 | 63 | /* Check the sanity of the synchronized clock by comparing its 64 | uncorrected frequency with the system monotonic clock. If 65 | the synchronized clock is the system clock, the measured 66 | frequency offset will be the current frequency correction of 67 | the system clock. */ 68 | 69 | if (!cc->freq_known) 70 | return ret; 71 | 72 | interval = (int64_t)ts - cc->last_ts; 73 | if (interval >= 0 && interval < CHECK_MIN_INTERVAL) 74 | return ret; 75 | 76 | clock_gettime(CLOCK_MONOTONIC, &now); 77 | mono_ts = now.tv_sec * 1000000000LL + now.tv_nsec; 78 | mono_interval = (int64_t)mono_ts - cc->last_mono_ts; 79 | 80 | if (mono_interval < CHECK_MIN_INTERVAL) 81 | return ret; 82 | 83 | if (cc->last_ts && cc->max_freq <= CHECK_MAX_FREQ) { 84 | max_foffset = 1e9 * (interval / 85 | (1.0 + cc->min_freq / 1e9) / 86 | mono_interval - 1.0); 87 | min_foffset = 1e9 * (interval / 88 | (1.0 + cc->max_freq / 1e9) / 89 | mono_interval - 1.0); 90 | 91 | if (min_foffset > cc->freq_limit) { 92 | pr_warning("clockcheck: clock jumped forward or" 93 | " running faster than expected!"); 94 | ret = 1; 95 | } else if (max_foffset < -cc->freq_limit) { 96 | pr_warning("clockcheck: clock jumped backward or" 97 | " running slower than expected!"); 98 | ret = 1; 99 | } 100 | } 101 | 102 | cc->last_mono_ts = mono_ts; 103 | cc->last_ts = ts; 104 | cc->max_freq = cc->min_freq = cc->current_freq; 105 | 106 | return ret; 107 | } 108 | 109 | void clockcheck_set_freq(struct clockcheck *cc, int freq) 110 | { 111 | if (cc->max_freq < freq) 112 | cc->max_freq = freq; 113 | if (cc->min_freq > freq) 114 | cc->min_freq = freq; 115 | cc->current_freq = freq; 116 | cc->freq_known = 1; 117 | } 118 | 119 | void clockcheck_step(struct clockcheck *cc, int64_t step) 120 | { 121 | if (cc->last_ts) 122 | cc->last_ts += step; 123 | } 124 | 125 | void clockcheck_destroy(struct clockcheck *cc) 126 | { 127 | free(cc); 128 | } 129 | -------------------------------------------------------------------------------- /clockcheck.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file clockcheck.h 3 | * @brief Implements clock sanity checking. 4 | * @note Copyright (C) 2013 Miroslav Lichvar 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_CLOCKCHECK_H 21 | #define HAVE_CLOCKCHECK_H 22 | 23 | #include 24 | 25 | /** Opaque type */ 26 | struct clockcheck; 27 | 28 | /** 29 | * Create a new instance of a clock sanity check. 30 | * @param freq_limit The maximum allowed frequency offset between uncorrected 31 | * clock and the system monotonic clock in ppb. 32 | * @return A pointer to a new clock check on success, NULL otherwise. 33 | */ 34 | struct clockcheck *clockcheck_create(int freq_limit); 35 | 36 | /** 37 | * Perform the sanity check on a time stamp. 38 | * @param cc Pointer to a clock check obtained via @ref clockcheck_create(). 39 | * @param ts Time stamp made by the clock in nanoseconds. 40 | * @return Zero if ts passed the check, non-zero otherwise. 41 | */ 42 | int clockcheck_sample(struct clockcheck *cc, uint64_t ts); 43 | 44 | /** 45 | * Inform clock check about changes in current frequency of the clock. 46 | * @param cc Pointer to a clock check obtained via @ref clockcheck_create(). 47 | * @param freq Frequency correction applied to the clock in ppb. 48 | */ 49 | void clockcheck_set_freq(struct clockcheck *cc, int freq); 50 | 51 | /** 52 | * Inform clock check that the clock was stepped. 53 | * @param cc Pointer to a clock check obtained via @ref clockcheck_create(). 54 | * @param step Step correction applied to the clock in nanoseconds. 55 | */ 56 | void clockcheck_step(struct clockcheck *cc, int64_t step); 57 | 58 | /** 59 | * Destroy a clock check. 60 | * @param cc Pointer to a clock check obtained via @ref clockcheck_create(). 61 | */ 62 | void clockcheck_destroy(struct clockcheck *cc); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file config.h 3 | * @brief Configuration file code 4 | * @note Copyright (C) 2011 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_CONFIG_H 21 | #define HAVE_CONFIG_H 22 | 23 | #include "ds.h" 24 | #include "dm.h" 25 | #include "filter.h" 26 | #include "transport.h" 27 | #include "servo.h" 28 | #include "sk.h" 29 | 30 | #define MAX_PORTS 8 31 | #define MAX_IFNAME_SIZE 108 /* = UNIX_PATH_MAX */ 32 | 33 | /** Defines a network interface, with PTP options. */ 34 | struct interface { 35 | char name[MAX_IFNAME_SIZE + 1]; 36 | enum delay_mechanism dm; 37 | enum transport_type transport; 38 | struct port_defaults pod; 39 | struct sk_ts_info ts_info; 40 | enum filter_type delay_filter; 41 | int delay_filter_length; 42 | }; 43 | 44 | #define CFG_IGNORE_DM (1 << 0) 45 | #define CFG_IGNORE_TRANSPORT (1 << 1) 46 | #define CFG_IGNORE_TIMESTAMPING (1 << 2) 47 | #define CFG_IGNORE_SLAVEONLY (1 << 3) 48 | #define CFG_IGNORE_PRINT_LEVEL (1 << 4) 49 | #define CFG_IGNORE_USE_SYSLOG (1 << 5) 50 | #define CFG_IGNORE_VERBOSE (1 << 6) 51 | 52 | enum parser_result { 53 | PARSED_OK, 54 | NOT_PARSED, 55 | BAD_VALUE, 56 | MALFORMED, 57 | OUT_OF_RANGE, 58 | }; 59 | 60 | struct config { 61 | /* configuration override */ 62 | int cfg_ignore; 63 | 64 | /* configured interfaces */ 65 | struct interface iface[MAX_PORTS]; 66 | int nports; 67 | 68 | enum timestamp_type timestamping; 69 | enum transport_type transport; 70 | enum delay_mechanism dm; 71 | 72 | struct default_ds dds; 73 | struct port_defaults pod; 74 | int *assume_two_step; 75 | int *tx_timestamp_timeout; 76 | int *check_fup_sync; 77 | 78 | enum servo_type clock_servo; 79 | 80 | double *step_threshold; 81 | double *first_step_threshold; 82 | int *max_frequency; 83 | 84 | double *pi_proportional_const; 85 | double *pi_integral_const; 86 | double *pi_proportional_scale; 87 | double *pi_proportional_exponent; 88 | double *pi_proportional_norm_max; 89 | double *pi_integral_scale; 90 | double *pi_integral_exponent; 91 | double *pi_integral_norm_max; 92 | int *sanity_freq_limit; 93 | 94 | unsigned char *ptp_dst_mac; 95 | unsigned char *p2p_dst_mac; 96 | unsigned char *udp6_scope; 97 | char *uds_address; 98 | 99 | int print_level; 100 | int use_syslog; 101 | int verbose; 102 | }; 103 | 104 | int config_read(char *name, struct config *cfg); 105 | int config_create_interface(char *name, struct config *cfg); 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /contain.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file contain.h 3 | * @brief Implements pseudo object oriented features. 4 | * @note Copyright (C) 2011 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_CONTAIN_H 21 | #define HAVE_CONTAIN_H 22 | 23 | #include 24 | 25 | /* 26 | * This macro boroughed from the Linux kernel. 27 | */ 28 | #define container_of(ptr, type, member) ({ \ 29 | const typeof( ((type *)0)->member ) *__mptr = (ptr); \ 30 | (type *)( (char *)__mptr - offsetof(type, member) ); \ 31 | }) 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /ddt.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ddt.h 3 | * @brief Derived data types 4 | * @note Copyright (C) 2011 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_DDT_H 21 | #define HAVE_DDT_H 22 | 23 | #include "pdt.h" 24 | 25 | #define PACKED __attribute__((packed)) 26 | 27 | typedef Integer64 TimeInterval; /* nanoseconds << 16 */ 28 | 29 | /** On the wire time stamp format. */ 30 | struct Timestamp { 31 | uint16_t seconds_msb; /* 16 bits + */ 32 | uint32_t seconds_lsb; /* 32 bits = 48 bits*/ 33 | UInteger32 nanoseconds; 34 | } PACKED; 35 | 36 | /** Internal binary time stamp format. */ 37 | struct timestamp { 38 | uint64_t sec; 39 | UInteger32 nsec; 40 | }; 41 | 42 | struct ClockIdentity { 43 | Octet id[8]; 44 | }; 45 | 46 | struct PortIdentity { 47 | struct ClockIdentity clockIdentity; 48 | UInteger16 portNumber; 49 | } PACKED; 50 | 51 | struct PortAddress { 52 | Enumeration16 networkProtocol; 53 | UInteger16 addressLength; 54 | Octet address[0]; 55 | } PACKED; 56 | 57 | struct PhysicalAddress { 58 | UInteger16 length; 59 | Octet address[0]; 60 | } PACKED; 61 | 62 | struct ClockQuality { 63 | UInteger8 clockClass; 64 | Enumeration8 clockAccuracy; 65 | UInteger16 offsetScaledLogVariance; 66 | } PACKED; 67 | 68 | struct TLV { 69 | Enumeration16 type; 70 | UInteger16 length; /* must be even */ 71 | Octet value[0]; 72 | } PACKED; 73 | 74 | struct PTPText { 75 | UInteger8 length; 76 | Octet text[0]; 77 | } PACKED; 78 | 79 | /* A static_ptp_text is like a PTPText but includes space to store the 80 | * text inside the struct. The text array must always be 81 | * null-terminated. Also tracks a maximum number of symbols. Note in 82 | * UTF-8, # symbols != # bytes. 83 | */ 84 | #define MAX_PTP_OCTETS 255 85 | struct static_ptp_text { 86 | /* null-terminated array of UTF-8 symbols */ 87 | Octet text[MAX_PTP_OCTETS + 1]; 88 | /* number of used bytes in text, not including trailing null */ 89 | int length; 90 | /* max number of UTF-8 symbols that can be in text */ 91 | int max_symbols; 92 | }; 93 | 94 | struct FaultRecord { 95 | UInteger16 faultRecordLength; 96 | struct Timestamp faultTime; 97 | Enumeration8 severityCode; 98 | struct PTPText faultName; 99 | struct PTPText faultValue; 100 | struct PTPText faultDescription; 101 | }; 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /default.cfg: -------------------------------------------------------------------------------- 1 | [global] 2 | # 3 | # Default Data Set 4 | # 5 | twoStepFlag 1 6 | slaveOnly 0 7 | priority1 128 8 | priority2 128 9 | domainNumber 0 10 | clockClass 248 11 | clockAccuracy 0xFE 12 | offsetScaledLogVariance 0xFFFF 13 | free_running 0 14 | freq_est_interval 1 15 | # 16 | # Port Data Set 17 | # 18 | logAnnounceInterval 1 19 | logSyncInterval 0 20 | logMinDelayReqInterval 0 21 | logMinPdelayReqInterval 0 22 | announceReceiptTimeout 3 23 | syncReceiptTimeout 0 24 | delayAsymmetry 0 25 | fault_reset_interval 4 26 | neighborPropDelayThresh 20000000 27 | # 28 | # Run time options 29 | # 30 | assume_two_step 0 31 | logging_level 6 32 | path_trace_enabled 0 33 | follow_up_info 0 34 | tx_timestamp_timeout 1 35 | use_syslog 1 36 | verbose 0 37 | summary_interval 0 38 | kernel_leap 1 39 | check_fup_sync 0 40 | # 41 | # Servo Options 42 | # 43 | pi_proportional_const 0.0 44 | pi_integral_const 0.0 45 | pi_proportional_scale 0.0 46 | pi_proportional_exponent -0.3 47 | pi_proportional_norm_max 0.7 48 | pi_integral_scale 0.0 49 | pi_integral_exponent 0.4 50 | pi_integral_norm_max 0.3 51 | step_threshold 0.0 52 | first_step_threshold 0.00002 53 | max_frequency 900000000 54 | clock_servo pi 55 | sanity_freq_limit 200000000 56 | # 57 | # Transport options 58 | # 59 | transportSpecific 0x0 60 | ptp_dst_mac 01:1B:19:00:00:00 61 | p2p_dst_mac 01:80:C2:00:00:0E 62 | udp6_scope 0x0E 63 | uds_address /var/run/ptp4l 64 | # 65 | # Default interface options 66 | # 67 | network_transport UDPv4 68 | delay_mechanism E2E 69 | time_stamping hardware 70 | delay_filter moving_median 71 | delay_filter_length 10 72 | # 73 | # Clock description 74 | # 75 | productDescription ;; 76 | revisionData ;; 77 | manufacturerIdentity 00:00:00 78 | userDescription ; 79 | timeSource 0xA0 80 | -------------------------------------------------------------------------------- /dm.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file dm.h 3 | * @brief Enumerates the delay mechanisms. 4 | * @note Copyright (C) 2012 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_DM_H 21 | #define HAVE_DM_H 22 | 23 | /** 24 | * Defines the possible delay mechanisms. 25 | */ 26 | enum delay_mechanism { 27 | 28 | /** Start as E2E, but switch to P2P if a peer is detected. */ 29 | DM_AUTO, 30 | 31 | /** Delay request-response mechanism. */ 32 | DM_E2E, 33 | 34 | /** Peer delay mechanism. */ 35 | DM_P2P, 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /ds.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ds.h 3 | * @brief Data sets 4 | * @note Copyright (C) 2011 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_DS_H 21 | #define HAVE_DS_H 22 | 23 | #include "ddt.h" 24 | #include "fault.h" 25 | #include "filter.h" 26 | 27 | /* clock data sets */ 28 | 29 | #define DDS_TWO_STEP_FLAG (1<<0) 30 | #define DDS_SLAVE_ONLY (1<<1) 31 | 32 | struct defaultDS { 33 | UInteger8 flags; 34 | UInteger8 reserved1; 35 | UInteger16 numberPorts; 36 | UInteger8 priority1; 37 | struct ClockQuality clockQuality; 38 | UInteger8 priority2; 39 | struct ClockIdentity clockIdentity; 40 | UInteger8 domainNumber; 41 | UInteger8 reserved2; 42 | } PACKED; 43 | 44 | #define OUI_LEN 3 45 | struct clock_description { 46 | struct static_ptp_text productDescription; 47 | struct static_ptp_text revisionData; 48 | struct static_ptp_text userDescription; 49 | Octet manufacturerIdentity[OUI_LEN]; 50 | }; 51 | 52 | struct default_ds { 53 | struct defaultDS dds; 54 | int free_running; 55 | int freq_est_interval; /*log seconds*/ 56 | int grand_master_capable; /*802.1AS only*/ 57 | int stats_interval; /*log seconds*/ 58 | int kernel_leap; 59 | int sanity_freq_limit; 60 | int time_source; 61 | struct clock_description clock_desc; 62 | enum filter_type delay_filter; 63 | int delay_filter_length; 64 | }; 65 | 66 | struct dataset { 67 | UInteger8 priority1; 68 | struct ClockIdentity identity; 69 | struct ClockQuality quality; 70 | UInteger8 priority2; 71 | UInteger16 stepsRemoved; 72 | struct PortIdentity sender; 73 | struct PortIdentity receiver; 74 | }; 75 | 76 | struct currentDS { 77 | UInteger16 stepsRemoved; 78 | TimeInterval offsetFromMaster; 79 | TimeInterval meanPathDelay; 80 | } PACKED; 81 | 82 | struct parentDS { 83 | struct PortIdentity parentPortIdentity; 84 | UInteger8 parentStats; 85 | UInteger8 reserved; 86 | UInteger16 observedParentOffsetScaledLogVariance; 87 | Integer32 observedParentClockPhaseChangeRate; 88 | UInteger8 grandmasterPriority1; 89 | struct ClockQuality grandmasterClockQuality; 90 | UInteger8 grandmasterPriority2; 91 | struct ClockIdentity grandmasterIdentity; 92 | } PACKED; 93 | 94 | struct parent_ds { 95 | struct parentDS pds; 96 | struct ClockIdentity *ptl; 97 | unsigned int path_length; 98 | }; 99 | 100 | #define CURRENT_UTC_OFFSET 35 /* 1 Jul 2012 */ 101 | #define INTERNAL_OSCILLATOR 0xA0 102 | 103 | struct timePropertiesDS { 104 | Integer16 currentUtcOffset; 105 | UInteger8 flags; 106 | Enumeration8 timeSource; 107 | } PACKED; 108 | 109 | struct portDS { 110 | struct PortIdentity portIdentity; 111 | Enumeration8 portState; 112 | Integer8 logMinDelayReqInterval; 113 | TimeInterval peerMeanPathDelay; 114 | Integer8 logAnnounceInterval; 115 | UInteger8 announceReceiptTimeout; 116 | Integer8 logSyncInterval; 117 | Enumeration8 delayMechanism; 118 | Integer8 logMinPdelayReqInterval; 119 | UInteger8 versionNumber; 120 | } PACKED; 121 | 122 | #define FRI_ASAP (-128) 123 | 124 | struct port_defaults { 125 | Integer64 asymmetry; 126 | Integer8 logAnnounceInterval; 127 | Integer8 logSyncInterval; 128 | Integer8 logMinDelayReqInterval; 129 | Integer8 logMinPdelayReqInterval; 130 | UInteger8 announceReceiptTimeout; 131 | UInteger8 syncReceiptTimeout; 132 | UInteger8 transportSpecific; 133 | int announce_span; 134 | int path_trace_enabled; 135 | int follow_up_info; 136 | int freq_est_interval; /*log seconds*/ 137 | unsigned int foreign_master_threshold; 138 | struct fault_interval flt_interval_pertype[FT_CNT]; 139 | UInteger32 neighborPropDelayThresh; /*nanoseconds*/ 140 | int min_neighbor_prop_delay; /*nanoseconds*/ 141 | }; 142 | 143 | #endif 144 | -------------------------------------------------------------------------------- /ether.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ether.h 3 | * @brief Provides definitions useful when working with Ethernet packets. 4 | * @note Copyright (C) 2012 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_ETHER_H 21 | #define HAVE_ETHER_H 22 | 23 | #include 24 | 25 | #define MAC_LEN 6 26 | #define PTP_DST_MAC 0x01, 0x1B, 0x19, 0x00, 0x00, 0x00 27 | #define P2P_DST_MAC 0x01, 0x80, 0xC2, 0x00, 0x00, 0x0E 28 | 29 | typedef uint8_t eth_addr[MAC_LEN]; 30 | 31 | struct eth_hdr { 32 | eth_addr dst; 33 | eth_addr src; 34 | uint16_t type; 35 | } __attribute__((packed)); 36 | 37 | #define VLAN_HLEN 4 38 | 39 | struct vlan_hdr { 40 | eth_addr dst; 41 | eth_addr src; 42 | uint16_t tpid; 43 | uint16_t tci; 44 | uint16_t type; 45 | } __attribute__((packed)); 46 | 47 | #define OFF_ETYPE (2 * sizeof(eth_addr)) 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /fault.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file fault.c 3 | * @note Copyright (C) 2013 Delio Brignoli 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include "fault.h" 20 | 21 | static const char *fault_type_str[FT_CNT] = { 22 | "FT_UNSPECIFIED", 23 | "FT_BAD_PEER_NETWORK", 24 | }; 25 | 26 | const char *ft_str(enum fault_type ft) 27 | { 28 | if (ft < 0 || ft >= FT_CNT) 29 | return "INVALID_FAULT_TYPE_ENUM"; 30 | return fault_type_str[ft]; 31 | } 32 | -------------------------------------------------------------------------------- /fault.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file fault.h 3 | * @note Copyright (C) 2013 Delio Brignoli 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include 20 | 21 | enum fault_type { 22 | FT_UNSPECIFIED = 0, 23 | FT_BAD_PEER_NETWORK, 24 | FT_CNT, 25 | }; 26 | 27 | enum fault_tmo_type { 28 | FTMO_LINEAR_SECONDS = 0, 29 | FTMO_LOG2_SECONDS, 30 | FTMO_CNT, 31 | }; 32 | 33 | struct fault_interval { 34 | enum fault_tmo_type type; 35 | int32_t val; 36 | }; 37 | 38 | const char *ft_str(enum fault_type ft); 39 | -------------------------------------------------------------------------------- /fd.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file fd.h 3 | * @brief Defines a array of file descriptors, useful for polling. 4 | * @note Copyright (C) 2011 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_FD_H 21 | #define HAVE_FD_H 22 | 23 | #define N_TIMER_FDS 6 24 | 25 | enum { 26 | FD_EVENT, 27 | FD_GENERAL, 28 | FD_ANNOUNCE_TIMER, 29 | FD_SYNC_RX_TIMER, 30 | FD_DELAY_TIMER, 31 | FD_QUALIFICATION_TIMER, 32 | FD_MANNO_TIMER, 33 | FD_SYNC_TX_TIMER, 34 | N_POLLFD, 35 | }; 36 | 37 | struct fdarray { 38 | int fd[N_POLLFD]; 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /filter.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file filter.c 3 | * @note Copyright (C) 2013 Miroslav Lichvar 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | 20 | #include "filter_private.h" 21 | #include "mave.h" 22 | #include "mmedian.h" 23 | 24 | struct filter *filter_create(enum filter_type type, int length) 25 | { 26 | switch (type) { 27 | case FILTER_MOVING_AVERAGE: 28 | return mave_create(length); 29 | case FILTER_MOVING_MEDIAN: 30 | return mmedian_create(length); 31 | default: 32 | return NULL; 33 | } 34 | } 35 | 36 | void filter_destroy(struct filter *filter) 37 | { 38 | filter->destroy(filter); 39 | } 40 | 41 | tmv_t filter_sample(struct filter *filter, tmv_t sample) 42 | { 43 | return filter->sample(filter, sample); 44 | } 45 | 46 | void filter_reset(struct filter *filter) 47 | { 48 | filter->reset(filter); 49 | } 50 | -------------------------------------------------------------------------------- /filter.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file filter.h 3 | * @brief Implements a generic filter interface. 4 | * @note Copyright (C) 2013 Miroslav Lichvar 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_FILTER_H 21 | #define HAVE_FILTER_H 22 | 23 | #include "tmv.h" 24 | 25 | /** Opaque type */ 26 | struct filter; 27 | 28 | /** 29 | * Defines the available filters. 30 | */ 31 | enum filter_type { 32 | FILTER_MOVING_AVERAGE, 33 | FILTER_MOVING_MEDIAN, 34 | }; 35 | 36 | /** 37 | * Create a new instance of a filter. 38 | * @param type The type of the filter to create. 39 | * @param length The filter's length. 40 | * @return A pointer to a new filter on success, NULL otherwise. 41 | */ 42 | struct filter *filter_create(enum filter_type type, int length); 43 | 44 | /** 45 | * Destroy an instance of a filter. 46 | * @param filter Pointer to a filter obtained via @ref filter_create(). 47 | */ 48 | void filter_destroy(struct filter *filter); 49 | 50 | /** 51 | * Feed a sample into a filter. 52 | * @param filter Pointer to a filter obtained via @ref filter_create(). 53 | * @param sample The input sample. 54 | * @return The output value. 55 | */ 56 | tmv_t filter_sample(struct filter *filter, tmv_t sample); 57 | 58 | /** 59 | * Reset a filter. 60 | * @param filter Pointer to a filter obtained via @ref filter_create(). 61 | */ 62 | void filter_reset(struct filter *filter); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /filter_private.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file filter_private.h 3 | * @note Copyright (C) 2013 Miroslav Lichvar 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #ifndef HAVE_FILTER_PRIVATE_H 20 | #define HAVE_FILTER_PRIVATE_H 21 | 22 | #include "tmv.h" 23 | #include "contain.h" 24 | 25 | struct filter { 26 | void (*destroy)(struct filter *filter); 27 | 28 | tmv_t (*sample)(struct filter *filter, tmv_t sample); 29 | 30 | void (*reset)(struct filter *filter); 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /foreign.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file foreign.h 3 | * @brief Defines a foreign clock record. 4 | * @note Copyright (C) 2011 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_FOREIGN_H 21 | #define HAVE_FOREIGN_H 22 | 23 | #include 24 | 25 | #include "ds.h" 26 | #include "port.h" 27 | 28 | struct foreign_clock { 29 | /** 30 | * Pointer to next foreign_clock in list. 31 | */ 32 | LIST_ENTRY(foreign_clock) list; 33 | 34 | /** 35 | * A list of received announce messages. 36 | * 37 | * The data set field, foreignMasterPortIdentity, is the 38 | * sourcePortIdentity of the first message. 39 | */ 40 | TAILQ_HEAD(messages, ptp_message) messages; 41 | 42 | /** 43 | * Number of elements in the message list, 44 | * aka foreignMasterAnnounceMessages. 45 | */ 46 | unsigned int n_messages; 47 | 48 | /** 49 | * Pointer to the associated port. 50 | */ 51 | struct port *port; 52 | 53 | /** 54 | * Contains the information from the latest announce message 55 | * in a form suitable for comparision in the BMCA. 56 | */ 57 | struct dataset dataset; 58 | }; 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /fsm.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file fsm.c 3 | * @note Copyright (C) 2011 Richard Cochran 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include "fsm.h" 20 | 21 | enum port_state ptp_fsm(enum port_state state, enum fsm_event event, int mdiff) 22 | { 23 | enum port_state next = state; 24 | 25 | if (EV_INITIALIZE == event || EV_POWERUP == event) 26 | return PS_INITIALIZING; 27 | 28 | switch (state) { 29 | case PS_INITIALIZING: 30 | next = PS_LISTENING; 31 | break; 32 | 33 | case PS_FAULTY: 34 | switch (event) { 35 | case EV_DESIGNATED_DISABLED: 36 | next = PS_DISABLED; 37 | break; 38 | case EV_FAULT_CLEARED: 39 | next = PS_INITIALIZING; 40 | break; 41 | default: 42 | break; 43 | } 44 | break; 45 | 46 | case PS_DISABLED: 47 | if (EV_DESIGNATED_ENABLED == event) 48 | next = PS_INITIALIZING; 49 | break; 50 | 51 | case PS_LISTENING: 52 | switch (event) { 53 | case EV_DESIGNATED_DISABLED: 54 | next = PS_DISABLED; 55 | break; 56 | case EV_FAULT_DETECTED: 57 | next = PS_FAULTY; 58 | break; 59 | case EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES: 60 | next = PS_MASTER; 61 | break; 62 | case EV_RS_MASTER: 63 | next = PS_PRE_MASTER; 64 | break; 65 | case EV_RS_GRAND_MASTER: 66 | next = PS_GRAND_MASTER; 67 | break; 68 | case EV_RS_SLAVE: 69 | next = PS_UNCALIBRATED; 70 | break; 71 | case EV_RS_PASSIVE: 72 | next = PS_PASSIVE; 73 | break; 74 | default: 75 | break; 76 | } 77 | break; 78 | 79 | case PS_PRE_MASTER: 80 | switch (event) { 81 | case EV_DESIGNATED_DISABLED: 82 | next = PS_DISABLED; 83 | break; 84 | case EV_FAULT_DETECTED: 85 | next = PS_FAULTY; 86 | break; 87 | case EV_QUALIFICATION_TIMEOUT_EXPIRES: 88 | next = PS_MASTER; 89 | break; 90 | case EV_RS_SLAVE: 91 | next = PS_UNCALIBRATED; 92 | break; 93 | case EV_RS_PASSIVE: 94 | next = PS_PASSIVE; 95 | break; 96 | default: 97 | break; 98 | } 99 | break; 100 | 101 | case PS_MASTER: 102 | case PS_GRAND_MASTER: 103 | switch (event) { 104 | case EV_DESIGNATED_DISABLED: 105 | next = PS_DISABLED; 106 | break; 107 | case EV_FAULT_DETECTED: 108 | next = PS_FAULTY; 109 | break; 110 | case EV_RS_SLAVE: 111 | next = PS_UNCALIBRATED; 112 | break; 113 | case EV_RS_PASSIVE: 114 | next = PS_PASSIVE; 115 | break; 116 | default: 117 | break; 118 | } 119 | break; 120 | 121 | case PS_PASSIVE: 122 | switch (event) { 123 | case EV_DESIGNATED_DISABLED: 124 | next = PS_DISABLED; 125 | break; 126 | case EV_FAULT_DETECTED: 127 | next = PS_FAULTY; 128 | break; 129 | case EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES: 130 | next = PS_MASTER; 131 | break; 132 | case EV_RS_MASTER: 133 | next = PS_PRE_MASTER; 134 | break; 135 | case EV_RS_GRAND_MASTER: 136 | next = PS_GRAND_MASTER; 137 | break; 138 | case EV_RS_SLAVE: 139 | next = PS_UNCALIBRATED; 140 | break; 141 | default: 142 | break; 143 | } 144 | break; 145 | 146 | case PS_UNCALIBRATED: 147 | switch (event) { 148 | case EV_DESIGNATED_DISABLED: 149 | next = PS_DISABLED; 150 | break; 151 | case EV_FAULT_DETECTED: 152 | next = PS_FAULTY; 153 | break; 154 | case EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES: 155 | next = PS_MASTER; 156 | break; 157 | case EV_MASTER_CLOCK_SELECTED: 158 | next = PS_SLAVE; 159 | break; 160 | case EV_RS_MASTER: 161 | next = PS_PRE_MASTER; 162 | break; 163 | case EV_RS_GRAND_MASTER: 164 | next = PS_GRAND_MASTER; 165 | break; 166 | case EV_RS_SLAVE: 167 | next = PS_UNCALIBRATED; 168 | break; 169 | case EV_RS_PASSIVE: 170 | next = PS_PASSIVE; 171 | break; 172 | default: 173 | break; 174 | } 175 | break; 176 | 177 | case PS_SLAVE: 178 | switch (event) { 179 | case EV_DESIGNATED_DISABLED: 180 | next = PS_DISABLED; 181 | break; 182 | case EV_FAULT_DETECTED: 183 | next = PS_FAULTY; 184 | break; 185 | case EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES: 186 | next = PS_MASTER; 187 | break; 188 | case EV_SYNCHRONIZATION_FAULT: 189 | next = PS_UNCALIBRATED; 190 | break; 191 | case EV_RS_MASTER: 192 | next = PS_PRE_MASTER; 193 | break; 194 | case EV_RS_GRAND_MASTER: 195 | next = PS_GRAND_MASTER; 196 | break; 197 | case EV_RS_SLAVE: 198 | if (mdiff) 199 | next = PS_UNCALIBRATED; 200 | break; 201 | case EV_RS_PASSIVE: 202 | next = PS_PASSIVE; 203 | break; 204 | default: 205 | break; 206 | } 207 | break; 208 | } 209 | 210 | return next; 211 | } 212 | 213 | enum port_state ptp_slave_fsm(enum port_state state, enum fsm_event event, 214 | int mdiff) 215 | { 216 | enum port_state next = state; 217 | 218 | if (EV_INITIALIZE == event || EV_POWERUP == event) 219 | return PS_INITIALIZING; 220 | 221 | switch (state) { 222 | case PS_INITIALIZING: 223 | next = PS_LISTENING; 224 | break; 225 | 226 | case PS_FAULTY: 227 | switch (event) { 228 | case EV_DESIGNATED_DISABLED: 229 | next = PS_DISABLED; 230 | break; 231 | case EV_FAULT_CLEARED: 232 | next = PS_INITIALIZING; 233 | break; 234 | default: 235 | break; 236 | } 237 | break; 238 | 239 | case PS_DISABLED: 240 | if (EV_DESIGNATED_ENABLED == event) 241 | next = PS_INITIALIZING; 242 | break; 243 | 244 | case PS_LISTENING: 245 | switch (event) { 246 | case EV_DESIGNATED_DISABLED: 247 | next = PS_DISABLED; 248 | break; 249 | case EV_FAULT_DETECTED: 250 | next = PS_FAULTY; 251 | break; 252 | case EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES: 253 | case EV_RS_MASTER: 254 | case EV_RS_GRAND_MASTER: 255 | case EV_RS_PASSIVE: 256 | next = PS_LISTENING; 257 | break; 258 | case EV_RS_SLAVE: 259 | next = PS_UNCALIBRATED; 260 | break; 261 | default: 262 | break; 263 | } 264 | break; 265 | 266 | case PS_UNCALIBRATED: 267 | switch (event) { 268 | case EV_DESIGNATED_DISABLED: 269 | next = PS_DISABLED; 270 | break; 271 | case EV_FAULT_DETECTED: 272 | next = PS_FAULTY; 273 | break; 274 | case EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES: 275 | case EV_RS_MASTER: 276 | case EV_RS_GRAND_MASTER: 277 | case EV_RS_PASSIVE: 278 | next = PS_LISTENING; 279 | break; 280 | case EV_MASTER_CLOCK_SELECTED: 281 | next = PS_SLAVE; 282 | break; 283 | default: 284 | break; 285 | } 286 | break; 287 | 288 | case PS_SLAVE: 289 | switch (event) { 290 | case EV_DESIGNATED_DISABLED: 291 | next = PS_DISABLED; 292 | break; 293 | case EV_FAULT_DETECTED: 294 | next = PS_FAULTY; 295 | break; 296 | case EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES: 297 | case EV_RS_MASTER: 298 | case EV_RS_GRAND_MASTER: 299 | case EV_RS_PASSIVE: 300 | next = PS_LISTENING; 301 | break; 302 | case EV_SYNCHRONIZATION_FAULT: 303 | next = PS_UNCALIBRATED; 304 | break; 305 | case EV_RS_SLAVE: 306 | if (mdiff) 307 | next = PS_UNCALIBRATED; 308 | break; 309 | default: 310 | break; 311 | } 312 | break; 313 | 314 | default: 315 | break; 316 | } 317 | 318 | return next; 319 | } 320 | -------------------------------------------------------------------------------- /fsm.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file fsm.h 3 | * @brief The finite state machine for ports on boundary and ordinary clocks. 4 | * @note Copyright (C) 2011 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_FSM_H 21 | #define HAVE_FSM_H 22 | 23 | /** Defines the state of a port. */ 24 | enum port_state { 25 | PS_INITIALIZING = 1, 26 | PS_FAULTY, 27 | PS_DISABLED, 28 | PS_LISTENING, 29 | PS_PRE_MASTER, 30 | PS_MASTER, 31 | PS_PASSIVE, 32 | PS_UNCALIBRATED, 33 | PS_SLAVE, 34 | PS_GRAND_MASTER, /*non-standard extension*/ 35 | }; 36 | 37 | /** Defines the events for the port state machine. */ 38 | enum fsm_event { 39 | EV_NONE, 40 | EV_POWERUP, 41 | EV_INITIALIZE, 42 | EV_DESIGNATED_ENABLED, 43 | EV_DESIGNATED_DISABLED, 44 | EV_FAULT_CLEARED, 45 | EV_FAULT_DETECTED, 46 | EV_STATE_DECISION_EVENT, 47 | EV_QUALIFICATION_TIMEOUT_EXPIRES, 48 | EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES, 49 | EV_SYNCHRONIZATION_FAULT, 50 | EV_MASTER_CLOCK_SELECTED, 51 | EV_RS_MASTER, 52 | EV_RS_GRAND_MASTER, 53 | EV_RS_SLAVE, 54 | EV_RS_PASSIVE, 55 | }; 56 | 57 | /** 58 | * Run the state machine for a BC or OC port. 59 | * @param state The current state of the port. 60 | * @param event The event to be processed. 61 | * @param mdiff Whether a new master has been selected. 62 | * @return The new state for the port. 63 | */ 64 | enum port_state ptp_fsm(enum port_state state, enum fsm_event event, int mdiff); 65 | 66 | /** 67 | * Run the state machine for a slave only clock. 68 | * @param state The current state of the port. 69 | * @param event The event to be processed. 70 | * @param mdiff Whether a new master has been selected. 71 | * @return The new state for the port. 72 | */ 73 | enum port_state ptp_slave_fsm(enum port_state state, enum fsm_event event, 74 | int mdiff); 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /gPTP.cfg: -------------------------------------------------------------------------------- 1 | [global] 2 | # 3 | # Default Data Set 4 | # 5 | twoStepFlag 1 6 | gmCapable 1 7 | priority1 248 8 | priority2 248 9 | domainNumber 0 10 | clockClass 248 11 | clockAccuracy 0xFE 12 | offsetScaledLogVariance 0xFFFF 13 | free_running 0 14 | freq_est_interval 1 15 | # 16 | # Port Data Set 17 | # 18 | logAnnounceInterval 1 19 | logSyncInterval -3 20 | logMinPdelayReqInterval 0 21 | announceReceiptTimeout 3 22 | syncReceiptTimeout 3 23 | delayAsymmetry 0 24 | fault_reset_interval 4 25 | neighborPropDelayThresh 800 26 | min_neighbor_prop_delay -20000000 27 | foreign_master_threshold 1 28 | # 29 | # Run time options 30 | # 31 | assume_two_step 1 32 | logging_level 6 33 | path_trace_enabled 1 34 | follow_up_info 1 35 | tx_timestamp_timeout 1 36 | use_syslog 1 37 | verbose 0 38 | summary_interval 0 39 | kernel_leap 1 40 | check_fup_sync 0 41 | # 42 | # Servo options 43 | # 44 | pi_proportional_const 0.0 45 | pi_integral_const 0.0 46 | pi_proportional_scale 0.0 47 | pi_proportional_exponent -0.3 48 | pi_proportional_norm_max 0.7 49 | pi_integral_scale 0.0 50 | pi_integral_exponent 0.4 51 | pi_integral_norm_max 0.3 52 | step_threshold 0.0 53 | first_step_threshold 0.00002 54 | max_frequency 900000000 55 | clock_servo pi 56 | sanity_freq_limit 200000000 57 | # 58 | # Transport options 59 | # 60 | transportSpecific 0x1 61 | ptp_dst_mac 01:80:C2:00:00:0E 62 | p2p_dst_mac 01:80:C2:00:00:0E 63 | uds_address /var/run/ptp4l 64 | # 65 | # Default interface options 66 | # 67 | network_transport L2 68 | delay_mechanism P2P 69 | time_stamping hardware 70 | delay_filter moving_median 71 | delay_filter_length 10 72 | -------------------------------------------------------------------------------- /hwstamp_ctl.8: -------------------------------------------------------------------------------- 1 | .TH HWSTAMP_CTL 8 "November 2012" "linuxptp" 2 | .SH NAME 3 | hwstamp_ctl \- set time stamping policy at the driver level 4 | 5 | .SH SYNOPSIS 6 | .B hwstamp_ctl 7 | .BI \-i " interface" 8 | [ 9 | .BI \-r " rx-filter" 10 | ] [ 11 | .BI \-t " tx-type" 12 | ] [ 13 | .B \-v 14 | ] 15 | 16 | .SH DESCRIPTION 17 | .B hwstamp_ctl 18 | is a program used to set the hardware time stamping policy at the network 19 | driver level with the 20 | .B SIOCSHWTSTAMP 21 | .BR ioctl (2). 22 | The 23 | .I tx-type 24 | and 25 | .I rx-filter 26 | values are hints to the driver what it is expected to do. If the requested 27 | fine-grained filtering for incoming packets is not supported, the driver may 28 | time stamp more than just the requested types of packets. 29 | 30 | This program is a debugging tool. The 31 | .BR ptp4l (8) 32 | program does not need this program to function, it will set the policy 33 | automatically as appropriate. 34 | 35 | .SH OPTIONS 36 | .TP 37 | .BI \-i " interface" 38 | Specify the network interface of which the policy should be changed. 39 | .TP 40 | .BI \-r " rx-filter" 41 | Specify which types of incoming packets should be time stamped, 42 | .I rx-filter 43 | is an integer value. 44 | .TP 45 | .BI \-t " tx-type" 46 | Enable or disable hardware time stamping for outgoing packets, 47 | .I tx-type 48 | is an integer value. 49 | .TP 50 | .BI \-h 51 | Display a help message and list of possible values for 52 | .I rx-filter 53 | and 54 | .IR tx-type . 55 | .TP 56 | .B \-v 57 | Prints the software version and exits. 58 | 59 | .SH SEE ALSO 60 | .BR ioctl (2), 61 | .BR ptp4l (8) 62 | -------------------------------------------------------------------------------- /hwstamp_ctl.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file hwstamp_ctl.c 3 | * @brief Utility program to set time stamping policy at the driver level. 4 | * @note Copyright (C) 2012 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "version.h" 33 | 34 | static void usage(char *progname) 35 | { 36 | fprintf(stderr, 37 | "\n" 38 | "usage: %s [options]\n\n" 39 | " -h prints this message and exits\n" 40 | " -i [device] interface device to use, for example 'eth0'\n" 41 | " -r [%d..%d] select receive time stamping:\n" 42 | "\t\t%2d time stamp no incoming packet at all\n" 43 | "\t\t%2d time stamp any incoming packet\n" 44 | "\t\t%2d (reserved value)\n" 45 | "\t\t%2d PTP v1, UDP, any kind of event packet\n" 46 | "\t\t%2d PTP v1, UDP, Sync packet\n" 47 | "\t\t%2d PTP v1, UDP, Delay_req packet\n" 48 | "\t\t%2d PTP v2, UDP, any kind of event packet\n" 49 | "\t\t%2d PTP v2, UDP, Sync packet\n" 50 | "\t\t%2d PTP v2, UDP, Delay_req packet\n" 51 | "\t\t%2d 802.AS1, Ethernet, any kind of event packet\n" 52 | "\t\t%2d 802.AS1, Ethernet, Sync packet\n" 53 | "\t\t%2d 802.AS1, Ethernet, Delay_req packet\n" 54 | "\t\t%2d PTP v2/802.AS1, any layer, any kind of event packet\n" 55 | "\t\t%2d PTP v2/802.AS1, any layer, Sync packet\n" 56 | "\t\t%2d PTP v2/802.AS1, any layer, Delay_req packet\n" 57 | " -t [%d|%d] disable or enable transmit time stamping\n" 58 | " -v prints the software version and exits\n" 59 | "\n", 60 | progname, 61 | HWTSTAMP_FILTER_NONE, HWTSTAMP_FILTER_PTP_V2_DELAY_REQ, 62 | HWTSTAMP_FILTER_NONE, 63 | HWTSTAMP_FILTER_ALL, 64 | HWTSTAMP_FILTER_SOME, 65 | HWTSTAMP_FILTER_PTP_V1_L4_EVENT, 66 | HWTSTAMP_FILTER_PTP_V1_L4_SYNC, 67 | HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ, 68 | HWTSTAMP_FILTER_PTP_V2_L4_EVENT, 69 | HWTSTAMP_FILTER_PTP_V2_L4_SYNC, 70 | HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ, 71 | HWTSTAMP_FILTER_PTP_V2_L2_EVENT, 72 | HWTSTAMP_FILTER_PTP_V2_L2_SYNC, 73 | HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ, 74 | HWTSTAMP_FILTER_PTP_V2_EVENT, 75 | HWTSTAMP_FILTER_PTP_V2_SYNC, 76 | HWTSTAMP_FILTER_PTP_V2_DELAY_REQ, 77 | HWTSTAMP_TX_OFF, 78 | HWTSTAMP_TX_ON); 79 | } 80 | 81 | int main(int argc, char *argv[]) 82 | { 83 | struct ifreq ifreq; 84 | struct hwtstamp_config cfg; 85 | char *device = NULL, *progname; 86 | int c, err, fd, rxopt = HWTSTAMP_FILTER_NONE, txopt = HWTSTAMP_TX_OFF; 87 | 88 | /* Process the command line arguments. */ 89 | progname = strrchr(argv[0], '/'); 90 | progname = progname ? 1+progname : argv[0]; 91 | while (EOF != (c = getopt(argc, argv, "hi:r:t:v"))) { 92 | switch (c) { 93 | case 'i': 94 | device = optarg; 95 | break; 96 | case 'r': 97 | rxopt = atoi(optarg); 98 | break; 99 | case 't': 100 | txopt = atoi(optarg); 101 | break; 102 | case 'v': 103 | version_show(stdout); 104 | return 0; 105 | case 'h': 106 | usage(progname); 107 | return 0; 108 | case '?': 109 | default: 110 | usage(progname); 111 | return -1; 112 | } 113 | } 114 | 115 | if (!device) { 116 | usage(progname); 117 | return -1; 118 | } 119 | if (rxopt < HWTSTAMP_FILTER_NONE || 120 | rxopt > HWTSTAMP_FILTER_PTP_V2_DELAY_REQ || 121 | txopt < HWTSTAMP_TX_OFF || txopt > HWTSTAMP_TX_ON) { 122 | usage(progname); 123 | return -1; 124 | } 125 | 126 | memset(&ifreq, 0, sizeof(ifreq)); 127 | memset(&cfg, 0, sizeof(cfg)); 128 | 129 | strncpy(ifreq.ifr_name, device, sizeof(ifreq.ifr_name)); 130 | 131 | ifreq.ifr_data = (void *) &cfg; 132 | cfg.tx_type = txopt; 133 | cfg.rx_filter = rxopt; 134 | 135 | fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); 136 | if (fd < 0) { 137 | perror("socket"); 138 | return -1; 139 | } 140 | 141 | err = ioctl(fd, SIOCSHWTSTAMP, &ifreq); 142 | if (err < 0) { 143 | err = errno; 144 | perror("SIOCSHWTSTAMP failed"); 145 | if (err == ERANGE) 146 | fprintf(stderr, "The requested time stamping mode is not supported by the hardware.\n"); 147 | } 148 | 149 | printf("tx_type %d\n" "rx_filter %d\n", cfg.tx_type, cfg.rx_filter); 150 | 151 | return err; 152 | } 153 | -------------------------------------------------------------------------------- /incdefs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Discover the CFLAGS to use during compilation. 4 | # 5 | # Copyright (C) 2013 Richard Cochran 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program; if not, write to the Free Software Foundation, Inc., 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | 21 | # 22 | # Look for the clock_adjtime functional prototype in the C library. 23 | # 24 | user_flags() 25 | { 26 | dirs=$(echo "" | ${CROSS_COMPILE}cpp -Wp,-v 2>&1 >/dev/null | grep ^" /") 27 | for d in $dirs; do 28 | files=$(find $d -type f -name time.h) 29 | for f in $files; do 30 | if grep -q clock_adjtime $f; then 31 | printf " -D_GNU_SOURCE -DHAVE_CLOCK_ADJTIME" 32 | return 33 | fi 34 | done 35 | done 36 | } 37 | 38 | # 39 | # Find the most appropriate kernel header for the SIOCSHWTSTAMP ioctl. 40 | # 41 | # 1. custom kernel or cross build using KBUILD_OUTPUT 42 | # 2. sanitized headers installed under /lib/modules/`uname -r`/build 43 | # 3. normal build using standard system headers 44 | # 45 | kernel_flags() 46 | { 47 | prefix="" 48 | tstamp=/usr/include/linux/net_tstamp.h 49 | 50 | if [ "x$KBUILD_OUTPUT" != "x" ]; then 51 | # With KBUILD_OUTPUT set, we are building against 52 | # either a custom kernel or a cross compiled kernel. 53 | build=${KBUILD_OUTPUT} 54 | else 55 | # If the currently running kernel is a custom build 56 | # with the headers installed, then we should use them. 57 | build=/lib/modules/`uname -r`/build 58 | fi 59 | 60 | if [ -f ${build}${tstamp} ]; then 61 | prefix=${build} 62 | printf " -I%s/usr/include" $prefix 63 | fi 64 | 65 | if grep -q HWTSTAMP_TX_ONESTEP_SYNC ${prefix}${tstamp}; then 66 | printf " -DHAVE_ONESTEP_SYNC" 67 | fi 68 | } 69 | 70 | flags="$(user_flags)$(kernel_flags)" 71 | echo "$flags" 72 | -------------------------------------------------------------------------------- /libmain.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file libmain.c 3 | * @brief PTP Boundary Clock library main file 4 | * @note Copyright (C) 2011 Richard Cochran 5 | * @note Modified by Delio Brignoli 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along 18 | * with this program; if not, write to the Free Software Foundation, Inc., 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | */ 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "clock.h" 30 | #include "config.h" 31 | #include "pi.h" 32 | #include "print.h" 33 | #include "raw.h" 34 | #include "sk.h" 35 | #include "transport.h" 36 | #include "udp6.h" 37 | #include "uds.h" 38 | #include "util.h" 39 | #include "version.h" 40 | 41 | int assume_two_step = 0; 42 | 43 | static struct config cfg_settings = { 44 | .dds = { 45 | .dds = { 46 | .flags = DDS_TWO_STEP_FLAG, 47 | .priority1 = 128, 48 | .clockQuality.clockClass = 248, 49 | .clockQuality.clockAccuracy = 0xfe, 50 | .clockQuality.offsetScaledLogVariance = 0xffff, 51 | .priority2 = 128, 52 | .domainNumber = 0, 53 | }, 54 | .free_running = 0, 55 | .freq_est_interval = 1, 56 | .grand_master_capable = 1, 57 | .stats_interval = 0, 58 | .kernel_leap = 1, 59 | .sanity_freq_limit = 200000000, 60 | .time_source = INTERNAL_OSCILLATOR, 61 | .clock_desc = { 62 | .productDescription = { 63 | .max_symbols = 64, 64 | .text = ";;", 65 | .length = 2, 66 | }, 67 | .revisionData = { 68 | .max_symbols = 32, 69 | .text = ";;", 70 | .length = 2, 71 | }, 72 | .userDescription = { .max_symbols = 128 }, 73 | .manufacturerIdentity = { 0, 0, 0 }, 74 | }, 75 | .delay_filter = FILTER_MOVING_MEDIAN, 76 | .delay_filter_length = 10, 77 | }, 78 | 79 | .pod = { 80 | .logAnnounceInterval = 1, 81 | .logSyncInterval = 0, 82 | .logMinDelayReqInterval = 0, 83 | .logMinPdelayReqInterval = 0, 84 | .announceReceiptTimeout = 3, 85 | .syncReceiptTimeout = 0, 86 | .transportSpecific = 0, 87 | .announce_span = 1, 88 | .path_trace_enabled = 0, 89 | .follow_up_info = 0, 90 | .freq_est_interval = 1, 91 | /* Default to very a large neighborPropDelay threshold */ 92 | .neighborPropDelayThresh = 20000000, 93 | .min_neighbor_prop_delay = -20000000, 94 | .foreign_master_threshold = 2, 95 | }, 96 | 97 | .timestamping = TS_HARDWARE, 98 | .dm = DM_E2E, 99 | .transport = TRANS_UDP_IPV4, 100 | 101 | .assume_two_step = &assume_two_step, 102 | .tx_timestamp_timeout = &sk_tx_timeout, 103 | .check_fup_sync = &sk_check_fupsync, 104 | 105 | .clock_servo = CLOCK_SERVO_PI, 106 | 107 | .step_threshold = &servo_step_threshold, 108 | .first_step_threshold = &servo_first_step_threshold, 109 | .max_frequency = &servo_max_frequency, 110 | 111 | .pi_proportional_const = &configured_pi_kp, 112 | .pi_integral_const = &configured_pi_ki, 113 | .pi_proportional_scale = &configured_pi_kp_scale, 114 | .pi_proportional_exponent = &configured_pi_kp_exponent, 115 | .pi_proportional_norm_max = &configured_pi_kp_norm_max, 116 | .pi_integral_scale = &configured_pi_ki_scale, 117 | .pi_integral_exponent = &configured_pi_ki_exponent, 118 | .pi_integral_norm_max = &configured_pi_ki_norm_max, 119 | 120 | .ptp_dst_mac = ptp_dst_mac, 121 | .p2p_dst_mac = p2p_dst_mac, 122 | .udp6_scope = &udp6_scope, 123 | .uds_address = uds_path, 124 | 125 | .print_level = LOG_INFO, 126 | .use_syslog = 1, 127 | .verbose = 0, 128 | 129 | .cfg_ignore = 0, 130 | }; 131 | 132 | struct clock *ptp4l_setup(char *config) 133 | { 134 | char *req_phc = NULL; 135 | int c, i; 136 | struct interface *iface = cfg_settings.iface; 137 | char *ports[MAX_PORTS]; 138 | int nports = 0; 139 | enum timestamp_type *timestamping = &cfg_settings.timestamping; 140 | struct clock *clock; 141 | struct defaultDS *ds = &cfg_settings.dds.dds; 142 | int phc_index = -1, required_modes = 0; 143 | 144 | /* Set fault timeouts to a default value */ 145 | for (i = 0; i < FT_CNT; i++) { 146 | cfg_settings.pod.flt_interval_pertype[i].type = FTMO_LOG2_SECONDS; 147 | cfg_settings.pod.flt_interval_pertype[i].val = 4; 148 | } 149 | 150 | if (config && (c = config_read(config, &cfg_settings))) { 151 | goto exit_err; 152 | } 153 | if (!cfg_settings.dds.grand_master_capable && 154 | ds->flags & DDS_SLAVE_ONLY) { 155 | fprintf(stderr, 156 | "Cannot mix 1588 slaveOnly with 802.1AS !gmCapable.\n"); 157 | goto exit_err; 158 | } 159 | if (!cfg_settings.dds.grand_master_capable || 160 | ds->flags & DDS_SLAVE_ONLY) { 161 | ds->clockQuality.clockClass = 255; 162 | } 163 | 164 | print_set_verbose(cfg_settings.verbose); 165 | print_set_syslog(cfg_settings.use_syslog); 166 | print_set_level(cfg_settings.print_level); 167 | 168 | for (i = 0; i < nports; i++) { 169 | if (config_create_interface(ports[i], &cfg_settings) < 0) { 170 | fprintf(stderr, "too many interfaces\n"); 171 | goto exit_err; 172 | } 173 | } 174 | 175 | if (!cfg_settings.nports) { 176 | fprintf(stderr, "no interface specified\n"); 177 | goto exit_err; 178 | } 179 | 180 | if (!(ds->flags & DDS_TWO_STEP_FLAG)) { 181 | switch (*timestamping) { 182 | case TS_SOFTWARE: 183 | case TS_LEGACY_HW: 184 | fprintf(stderr, "one step is only possible " 185 | "with hardware time stamping\n"); 186 | goto exit_err; 187 | case TS_HARDWARE: 188 | *timestamping = TS_ONESTEP; 189 | break; 190 | case TS_ONESTEP: 191 | break; 192 | } 193 | } 194 | 195 | switch (*timestamping) { 196 | case TS_SOFTWARE: 197 | required_modes |= SOF_TIMESTAMPING_TX_SOFTWARE | 198 | SOF_TIMESTAMPING_RX_SOFTWARE | 199 | SOF_TIMESTAMPING_SOFTWARE; 200 | break; 201 | case TS_LEGACY_HW: 202 | required_modes |= SOF_TIMESTAMPING_TX_HARDWARE | 203 | SOF_TIMESTAMPING_RX_HARDWARE | 204 | SOF_TIMESTAMPING_SYS_HARDWARE; 205 | break; 206 | case TS_HARDWARE: 207 | case TS_ONESTEP: 208 | required_modes |= SOF_TIMESTAMPING_TX_HARDWARE | 209 | SOF_TIMESTAMPING_RX_HARDWARE | 210 | SOF_TIMESTAMPING_RAW_HARDWARE; 211 | break; 212 | } 213 | 214 | /* check whether timestamping mode is supported. */ 215 | for (i = 0; i < cfg_settings.nports; i++) { 216 | if (iface[i].ts_info.valid && 217 | ((iface[i].ts_info.so_timestamping & required_modes) != required_modes)) { 218 | fprintf(stderr, "interface '%s' does not support " 219 | "requested timestamping mode.\n", 220 | iface[i].name); 221 | goto exit_err; 222 | } 223 | } 224 | 225 | /* determine PHC Clock index */ 226 | if (cfg_settings.dds.free_running) { 227 | phc_index = -1; 228 | } else if (*timestamping == TS_SOFTWARE || *timestamping == TS_LEGACY_HW) { 229 | phc_index = -1; 230 | } else if (req_phc) { 231 | if (1 != sscanf(req_phc, "/dev/ptp%d", &phc_index)) { 232 | fprintf(stderr, "bad ptp device string\n"); 233 | goto exit_err; 234 | } 235 | } else if (iface[0].ts_info.valid) { 236 | phc_index = iface[0].ts_info.phc_index; 237 | } else { 238 | fprintf(stderr, "ptp device not specified and\n" 239 | "automatic determination is not\n" 240 | "supported. please specify ptp device\n"); 241 | goto exit_err; 242 | } 243 | 244 | if (phc_index >= 0) { 245 | pr_info("selected /dev/ptp%d as PTP clock", phc_index); 246 | } 247 | 248 | if (generate_clock_identity(&ds->clockIdentity, iface[0].name)) { 249 | fprintf(stderr, "failed to generate a clock identity\n"); 250 | goto exit_err; 251 | } 252 | 253 | clock = clock_create(phc_index, iface, cfg_settings.nports, 254 | *timestamping, &cfg_settings.dds, 255 | cfg_settings.clock_servo); 256 | if (!clock) { 257 | fprintf(stderr, "failed to create a clock\n"); 258 | goto exit_err; 259 | } 260 | 261 | return clock; 262 | 263 | exit_err: 264 | return NULL; 265 | } 266 | 267 | void ptp4l_destroy(struct clock *clock) 268 | { 269 | clock_destroy(clock); 270 | } 271 | -------------------------------------------------------------------------------- /libmain.h: -------------------------------------------------------------------------------- 1 | 2 | struct clock; 3 | 4 | struct clock *ptp4l_setup(char *config); 5 | void ptp4l_destroy(struct clock *clock); -------------------------------------------------------------------------------- /linreg.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file linreg.h 3 | * @note Copyright (C) 2014 Miroslav Lichvar 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #ifndef HAVE_LINREG_H 20 | #define HAVE_LINREG_H 21 | 22 | #include "servo.h" 23 | 24 | struct servo *linreg_servo_create(int fadj); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2011 Richard Cochran 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | KBUILD_OUTPUT = 19 | 20 | DEBUG = 21 | CC = $(CROSS_COMPILE)gcc 22 | VER = -DVER=$(version) 23 | CFLAGS = -Wall $(VER) $(incdefs) $(DEBUG) $(EXTRA_CFLAGS) 24 | LDLIBS = -lm -lrt $(EXTRA_LDFLAGS) 25 | PRG = ptp4l pmc phc2sys hwstamp_ctl ptp4l_asi 26 | OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o fault.o \ 27 | filter.o fsm.o linreg.o mave.o mmedian.o msg.o phc.o pi.o port.o print.o raw.o \ 28 | servo.o sk.o stats.o tlv.o transport.o udp.o udp6.o uds.o util.o version.o 29 | 30 | OBJECTS = $(OBJ) hwstamp_ctl.o phc2sys.o pmc.o pmc_common.o sysoff.o ptp4l.o \ 31 | libmain.o ptp4l_asi.o 32 | SRC = $(OBJECTS:.o=.c) 33 | DEPEND = $(OBJECTS:.o=.d) 34 | srcdir := $(dir $(lastword $(MAKEFILE_LIST))) 35 | incdefs := $(shell $(srcdir)/incdefs.sh) 36 | version := $(shell $(srcdir)/version.sh $(srcdir)) 37 | VPATH = $(srcdir) 38 | 39 | prefix = /usr/local 40 | sbindir = $(prefix)/sbin 41 | mandir = $(prefix)/man 42 | man8dir = $(mandir)/man8 43 | 44 | all: $(PRG) ptp4l.so 45 | 46 | ptp4l.so: $(OBJ) libmain.o 47 | $(CC) --shared $^ -o $@ 48 | 49 | ptp4l: $(OBJ) ptp4l.o 50 | 51 | ptp4l_asi: $(OBJ) ptp4l_asi.o libmain.o 52 | $(CC) $(LDLIBS) -lubus -lubox $^ -o $@ 53 | 54 | pmc: msg.o pmc.o pmc_common.o print.o raw.o sk.o tlv.o transport.o udp.o \ 55 | udp6.o uds.o util.o version.o 56 | 57 | phc2sys: clockadj.o clockcheck.o linreg.o msg.o phc.o phc2sys.o pi.o \ 58 | pmc_common.o print.o raw.o servo.o sk.o stats.o sysoff.o tlv.o \ 59 | transport.o udp.o udp6.o uds.o util.o version.o 60 | 61 | hwstamp_ctl: hwstamp_ctl.o version.o 62 | 63 | version.o: .version version.sh $(filter-out version.d,$(DEPEND)) 64 | 65 | .version: force 66 | @echo $(version) > .version.new; \ 67 | cmp -s .version .version.new || cp .version.new .version; \ 68 | rm -f .version.new; 69 | 70 | force: 71 | 72 | install: $(PRG) 73 | mkdir -p $(sbindir) $(man8dir) 74 | install $(PRG) $(sbindir) 75 | install -p -m 644 -t $(man8dir) $(PRG:%=%.8) 76 | 77 | clean: 78 | rm -f $(OBJECTS) $(DEPEND) 79 | 80 | distclean: clean 81 | rm -f $(PRG) 82 | rm -f .version 83 | 84 | # Implicit rule to generate a C source file's dependencies. 85 | %.d: %.c 86 | @echo DEPEND $<; \ 87 | rm -f $@; \ 88 | $(CC) -MM $(CPPFLAGS) $(CFLAGS) $< > $@.$$$$; \ 89 | sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ 90 | rm -f $@.$$$$ 91 | 92 | ifneq ($(MAKECMDGOALS), clean) 93 | ifneq ($(MAKECMDGOALS), distclean) 94 | -include $(DEPEND) 95 | endif 96 | endif 97 | 98 | .PHONY: all force clean distclean 99 | -------------------------------------------------------------------------------- /mave.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file mave.c 3 | * @note Copyright (C) 2011 Richard Cochran 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include 20 | #include 21 | 22 | #include "mave.h" 23 | #include "filter_private.h" 24 | 25 | struct mave { 26 | struct filter filter; 27 | int cnt; 28 | int len; 29 | int index; 30 | tmv_t sum; 31 | tmv_t *val; 32 | }; 33 | 34 | static void mave_destroy(struct filter *filter) 35 | { 36 | struct mave *m = container_of(filter, struct mave, filter); 37 | free(m->val); 38 | free(m); 39 | } 40 | 41 | static tmv_t mave_accumulate(struct filter *filter, tmv_t val) 42 | { 43 | struct mave *m = container_of(filter, struct mave, filter); 44 | 45 | m->sum = tmv_sub(m->sum, m->val[m->index]); 46 | m->val[m->index] = val; 47 | m->index = (1 + m->index) % m->len; 48 | m->sum = tmv_add(m->sum, val); 49 | if (m->cnt < m->len) { 50 | m->cnt++; 51 | } 52 | return tmv_div(m->sum, m->cnt); 53 | } 54 | 55 | static void mave_reset(struct filter *filter) 56 | { 57 | struct mave *m = container_of(filter, struct mave, filter); 58 | 59 | m->cnt = 0; 60 | m->index = 0; 61 | m->sum = 0; 62 | memset(m->val, 0, m->len * sizeof(*m->val)); 63 | } 64 | 65 | struct filter *mave_create(int length) 66 | { 67 | struct mave *m; 68 | m = calloc(1, sizeof(*m)); 69 | if (!m) { 70 | return NULL; 71 | } 72 | m->filter.destroy = mave_destroy; 73 | m->filter.sample = mave_accumulate; 74 | m->filter.reset = mave_reset; 75 | m->val = calloc(1, length * sizeof(*m->val)); 76 | if (!m->val) { 77 | free(m); 78 | return NULL; 79 | } 80 | m->len = length; 81 | return &m->filter; 82 | } 83 | -------------------------------------------------------------------------------- /mave.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file mave.h 3 | * @brief Implements a moving average. 4 | * @note Copyright (C) 2011 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_MAVE_H 21 | #define HAVE_MAVE_H 22 | 23 | #include "filter.h" 24 | 25 | struct filter *mave_create(int length); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /missing.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file missing.h 3 | * @note Copyright (C) 2011 Richard Cochran 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | 20 | /* 21 | * When glibc offers the syscall, this will go away. 22 | */ 23 | #ifndef HAVE_MISSING_H 24 | #define HAVE_MISSING_H 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #ifndef ADJ_NANO 32 | #define ADJ_NANO 0x2000 33 | #endif 34 | 35 | #ifndef ADJ_SETOFFSET 36 | #define ADJ_SETOFFSET 0x0100 37 | #endif 38 | 39 | #ifndef CLOCK_INVALID 40 | #define CLOCK_INVALID -1 41 | #endif 42 | 43 | #define CLOCKFD 3 44 | #define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD) 45 | #define CLOCKID_TO_FD(clk) ((unsigned int) ~((clk) >> 3)) 46 | 47 | #ifndef HAVE_ONESTEP_SYNC 48 | enum _missing_hwtstamp_tx_types { 49 | HWTSTAMP_TX_ONESTEP_SYNC = 2, 50 | }; 51 | #endif 52 | 53 | #ifndef HAVE_CLOCK_ADJTIME 54 | static inline int clock_adjtime(clockid_t id, struct timex *tx) 55 | { 56 | return syscall(__NR_clock_adjtime, id, tx); 57 | } 58 | #endif 59 | 60 | #ifndef __uClinux__ 61 | 62 | #include 63 | 64 | #else 65 | 66 | static inline int clock_nanosleep(clockid_t clock_id, int flags, 67 | const struct timespec *request, 68 | struct timespec *remain) 69 | { 70 | return syscall(__NR_clock_nanosleep, clock_id, flags, request, remain); 71 | } 72 | 73 | static inline int timerfd_create(int clockid, int flags) 74 | { 75 | return syscall(__NR_timerfd_create, clockid, flags); 76 | } 77 | 78 | static inline int timerfd_settime(int fd, int flags, 79 | const struct itimerspec *new_value, 80 | struct itimerspec *old_value) 81 | { 82 | return syscall(__NR_timerfd_settime, fd, flags, new_value, old_value); 83 | } 84 | 85 | #endif 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /mmedian.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file mmedian.c 3 | * @note Copyright (C) 2013 Miroslav Lichvar 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include 20 | #include 21 | 22 | #include "mmedian.h" 23 | #include "filter_private.h" 24 | 25 | struct mmedian { 26 | struct filter filter; 27 | int cnt; 28 | int len; 29 | int index; 30 | /* Indices sorted by value. */ 31 | int *order; 32 | /* Values stored in circular buffer. */ 33 | tmv_t *samples; 34 | }; 35 | 36 | static void mmedian_destroy(struct filter *filter) 37 | { 38 | struct mmedian *m = container_of(filter, struct mmedian, filter); 39 | free(m->order); 40 | free(m->samples); 41 | free(m); 42 | } 43 | 44 | static tmv_t mmedian_sample(struct filter *filter, tmv_t sample) 45 | { 46 | struct mmedian *m = container_of(filter, struct mmedian, filter); 47 | int i; 48 | 49 | m->samples[m->index] = sample; 50 | if (m->cnt < m->len) { 51 | m->cnt++; 52 | } else { 53 | /* Remove index of the replaced value from order. */ 54 | for (i = 0; i < m->cnt; i++) 55 | if (m->order[i] == m->index) 56 | break; 57 | for (; i + 1 < m->cnt; i++) 58 | m->order[i] = m->order[i + 1]; 59 | } 60 | 61 | /* Insert index of the new value to order. */ 62 | for (i = m->cnt - 1; i > 0; i--) { 63 | if (m->samples[m->order[i - 1]] <= m->samples[m->index]) 64 | break; 65 | m->order[i] = m->order[i - 1]; 66 | } 67 | m->order[i] = m->index; 68 | 69 | m->index = (1 + m->index) % m->len; 70 | 71 | if (m->cnt % 2) 72 | return m->samples[m->order[m->cnt / 2]]; 73 | else 74 | return tmv_div(tmv_add(m->samples[m->order[m->cnt / 2 - 1]], 75 | m->samples[m->order[m->cnt / 2]]), 2); 76 | } 77 | 78 | static void mmedian_reset(struct filter *filter) 79 | { 80 | struct mmedian *m = container_of(filter, struct mmedian, filter); 81 | m->cnt = 0; 82 | m->index = 0; 83 | } 84 | 85 | struct filter *mmedian_create(int length) 86 | { 87 | struct mmedian *m; 88 | 89 | if (length < 1) 90 | return NULL; 91 | m = calloc(1, sizeof(*m)); 92 | if (!m) 93 | return NULL; 94 | m->filter.destroy = mmedian_destroy; 95 | m->filter.sample = mmedian_sample; 96 | m->filter.reset = mmedian_reset; 97 | m->order = calloc(1, length * sizeof(*m->order)); 98 | if (!m->order) { 99 | free(m); 100 | return NULL; 101 | } 102 | m->samples = calloc(1, length * sizeof(*m->samples)); 103 | if (!m->samples) { 104 | free(m->order); 105 | free(m); 106 | return NULL; 107 | } 108 | m->len = length; 109 | return &m->filter; 110 | } 111 | -------------------------------------------------------------------------------- /mmedian.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file mmedian.h 3 | * @brief Implements a moving median. 4 | * @note Copyright (C) 2013 Miroslav Lichvar 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_MMEDIAN_H 21 | #define HAVE_MMEDIAN_H 22 | 23 | #include "filter.h" 24 | 25 | struct filter *mmedian_create(int length); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /notification.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file notification.h 3 | * @brief Definitions for the notification framework. 4 | * @note Copyright (C) 2014 Red Hat, Inc., Jiri Benc 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_NOTIFICATION_H 21 | #define HAVE_NOTIFICATION_H 22 | 23 | enum notification { 24 | NOTIFY_PORT_STATE, 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /pdt.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file pdt.h 3 | * @brief Primitive data types 4 | * @note Copyright (C) 2011 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_PDT_H 21 | #define HAVE_PDT_H 22 | 23 | #include 24 | 25 | enum {FALSE, TRUE}; 26 | 27 | typedef int Boolean; 28 | typedef uint8_t Enumeration8; 29 | typedef uint16_t Enumeration16; 30 | typedef int8_t Integer8; 31 | typedef uint8_t UInteger8; 32 | typedef int16_t Integer16; 33 | typedef uint16_t UInteger16; 34 | typedef int32_t Integer32; 35 | typedef uint32_t UInteger32; 36 | typedef int64_t Integer64; 37 | typedef uint8_t Octet; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /phc.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file phc.c 3 | * @note Copyright (C) 2011 Richard Cochran 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include "phc.h" 29 | 30 | /* 31 | * On 32 bit platforms, the PHC driver's maximum adjustment (type 32 | * 'int' in units of ppb) can overflow the timex.freq field (type 33 | * 'long'). So in this case we clamp the maximum to the largest 34 | * possible adjustment that fits into a 32 bit long. 35 | */ 36 | #define BITS_PER_LONG (sizeof(long)*8) 37 | #define MAX_PPB_32 32767999 /* 2^31 - 1 / 65.536 */ 38 | 39 | static int phc_get_caps(clockid_t clkid, struct ptp_clock_caps *caps); 40 | 41 | clockid_t phc_open(char *phc) 42 | { 43 | clockid_t clkid; 44 | struct ptp_clock_caps caps; 45 | int fd = open(phc, O_RDWR); 46 | 47 | if (fd < 0) 48 | return CLOCK_INVALID; 49 | 50 | clkid = FD_TO_CLOCKID(fd); 51 | /* check if clkid is valid */ 52 | if (phc_get_caps(clkid, &caps)) { 53 | close(fd); 54 | return CLOCK_INVALID; 55 | } 56 | 57 | return clkid; 58 | } 59 | 60 | void phc_close(clockid_t clkid) 61 | { 62 | if (clkid == CLOCK_INVALID) 63 | return; 64 | 65 | close(CLOCKID_TO_FD(clkid)); 66 | } 67 | 68 | static int phc_get_caps(clockid_t clkid, struct ptp_clock_caps *caps) 69 | { 70 | int fd = CLOCKID_TO_FD(clkid), err; 71 | 72 | err = ioctl(fd, PTP_CLOCK_GETCAPS, caps); 73 | if (err) 74 | perror("PTP_CLOCK_GETCAPS"); 75 | return err; 76 | } 77 | 78 | int phc_max_adj(clockid_t clkid) 79 | { 80 | int max; 81 | struct ptp_clock_caps caps; 82 | 83 | if (phc_get_caps(clkid, &caps)) 84 | return 0; 85 | 86 | max = caps.max_adj; 87 | 88 | if (BITS_PER_LONG == 32 && max > MAX_PPB_32) 89 | max = MAX_PPB_32; 90 | 91 | return max; 92 | } 93 | 94 | int phc_has_pps(clockid_t clkid) 95 | { 96 | struct ptp_clock_caps caps; 97 | 98 | if (phc_get_caps(clkid, &caps)) 99 | return 0; 100 | return caps.pps; 101 | } 102 | -------------------------------------------------------------------------------- /phc.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file phc.h 3 | * @note Copyright (C) 2011 Richard Cochran 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #ifndef HAVE_PHC_H 20 | #define HAVE_PHC_H 21 | 22 | #include "missing.h" 23 | 24 | /** 25 | * Opens a PTP hardware clock device. 26 | * 27 | * @param phc The device to open. 28 | * 29 | * @return A valid clock ID on success, CLOCK_INVALID otherwise. 30 | */ 31 | clockid_t phc_open(char *phc); 32 | 33 | /** 34 | * Closes a PTP hardware clock device. 35 | * 36 | * @param clkid A clock ID obtained using phc_open(). 37 | */ 38 | void phc_close(clockid_t clkid); 39 | 40 | /** 41 | * Query the maximum frequency adjustment of a PTP hardware clock device. 42 | * 43 | * @param clkid A clock ID obtained using phc_open(). 44 | * 45 | * @return The clock's maximum frequency adjustment in parts per billion. 46 | */ 47 | int phc_max_adj(clockid_t clkid); 48 | 49 | /** 50 | * Checks whether the given PTP hardware clock device supports PPS output. 51 | * 52 | * @param clkid A clock ID obtained using phc_open(). 53 | * 54 | * @return Zero if PPS output is not supported by the clock, non-zero 55 | * otherwise. 56 | */ 57 | int phc_has_pps(clockid_t clkid); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /phc2sys.8: -------------------------------------------------------------------------------- 1 | .TH PHC2SYS 8 "November 2012" "linuxptp" 2 | .SH NAME 3 | phc2sys \- synchronize two clocks 4 | 5 | .SH SYNOPSIS 6 | .B phc2sys 7 | [ 8 | .B \-wmqvx 9 | ] [ 10 | .BI \-d " pps-device" 11 | ] [ 12 | .BI \-s " device" 13 | ] [ 14 | .BI \-c " device" 15 | ] [ 16 | .BI \-O " offset" 17 | ] [ 18 | .BI \-E " servo" 19 | ] [ 20 | .BI \-P " kp" 21 | ] [ 22 | .BI \-I " ki" 23 | ] [ 24 | .BI \-S " step" 25 | ] [ 26 | .BI \-F " step" 27 | ] [ 28 | .BI \-R " update-rate" 29 | ] [ 30 | .BI \-N " clock-readings" 31 | ] [ 32 | .BI \-L " freq-limit" 33 | ] [ 34 | .BI \-u " summary-updates" 35 | ] [ 36 | .BI \-n " domain-number" 37 | ] [ 38 | .BI \-l " print-level" 39 | ] 40 | 41 | .SH DESCRIPTION 42 | .B phc2sys 43 | is a program which synchronizes two clocks in the system. Typically, it is used 44 | to synchronize the system clock to a PTP hardware clock (PHC), which itself is 45 | synchronized by the 46 | .BR ptp4l (8) 47 | program. 48 | 49 | Two synchronization modes are supported, one uses a pulse per second (PPS) 50 | signal provided by the source clock and the other mode reads time from the 51 | source clock directly. Some clocks can be used in both modes, the mode which 52 | will synchronize the slave clock with better accuracy depends on hardware and 53 | driver implementation. 54 | 55 | .SH OPTIONS 56 | .TP 57 | .BI \-d " pps-device" 58 | Specify the PPS device of the master clock (e.g. /dev/pps0). With this option 59 | the PPS synchronization mode is used instead of the direct mode. As the PPS 60 | signal does not specify time and only marks start of a second, the slave clock 61 | should be already close to the correct time before 62 | .B phc2sys 63 | is started or the 64 | .B \-s 65 | option should be used too. With the 66 | .B \-s 67 | option the PPS signal of the master clock is enabled automatically, otherwise 68 | it has to be enabled before 69 | .B phc2sys 70 | is started (e.g. by running \f(CWecho 1 > /sys/class/ptp/ptp0/pps_enable\fP). 71 | This option can be used only with the system clock as the slave clock. 72 | .TP 73 | .BI \-s " device" 74 | Specify the master clock by device (e.g. /dev/ptp0) or interface (e.g. eth0) or 75 | by name (e.g. CLOCK_REALTIME for the system clock). When this option is used 76 | together with the 77 | .B \-d 78 | option, the master clock is used only to correct the offset by whole number of 79 | seconds, which cannot be fixed with PPS alone. 80 | .TP 81 | .BI \-i " interface" 82 | Performs the exact same function as 83 | .B \-s 84 | for compatibility reasons. Previously enabled specifying master clock by network 85 | interface. However, this can now be done using 86 | .B \-s 87 | and this option is no longer necessary. As such it has been deprecated, and 88 | should no longer be used. 89 | .TP 90 | .BI \-c " device" 91 | Specify the slave clock by device (e.g. /dev/ptp1) or interface (e.g. eth1) or 92 | by name. The default is CLOCK_REALTIME (the system clock). 93 | .TP 94 | .BI \-E " servo" 95 | Specify which clock servo should be used. Valid values are pi for a PI 96 | controller and linreg for an adaptive controller using linear regression. 97 | The default is pi. 98 | .TP 99 | .BI \-P " kp" 100 | Specify the proportional constant of the PI controller. The default is 0.7. 101 | .TP 102 | .BI \-I " ki" 103 | Specify the integral constant of the PI controller. The default is 0.3. 104 | .TP 105 | .BI \-S " step" 106 | Specify the step threshold of the servo. It is the maximum offset that 107 | the servo corrects by changing the clock frequency instead of stepping the 108 | clock. The clock is stepped on start regardless of the option if the offset is 109 | larger than 20 microseconds (unless the 110 | .BI \-F 111 | option is used). It's specified in seconds. The value of 0.0 disables stepping 112 | after the start. The default is 0.0. 113 | .TP 114 | .BI \-F " step" 115 | Specify the step threshold applied only on the first update. It is the maximum 116 | offset that is corrected by adjusting clock. It's specified in seconds. The 117 | value of 0.0 disables stepping on start. 118 | The default is 0.00002 (20 microseconds). 119 | .TP 120 | .BI \-R " update-rate" 121 | Specify the slave clock update rate when running in the direct synchronization 122 | mode. The default is 1 per second. 123 | .TP 124 | .BI \-N " phc-num" 125 | Specify the number of master clock readings per one slave clock update. Only 126 | the fastest reading is used to update the slave clock, this is useful to 127 | minimize the error caused by random delays in scheduling and bus utilization. 128 | The default is 5. 129 | .TP 130 | .BI \-O " offset" 131 | Specify the offset between the slave and master times in seconds. See 132 | .SM 133 | .B TIME SCALE USAGE 134 | below. 135 | .TP 136 | .BI \-L " freq-limit" 137 | The maximum allowed frequency offset between uncorrected clock and the system 138 | monotonic clock in parts per billion (ppb). This is used as a sanity check of 139 | the synchronized clock. When a larger offset is measured, a warning message 140 | will be printed and the servo will be reset. When set to 0, the sanity check is 141 | disabled. The default is 200000000 (20%). 142 | .TP 143 | .BI \-u " summary-updates" 144 | Specify the number of clock updates included in summary statistics. The 145 | statistics include offset root mean square (RMS), maximum absolute offset, 146 | frequency offset mean and standard deviation, and mean of the delay in clock 147 | readings and standard deviation. The units are nanoseconds and parts per 148 | billion (ppb). If zero, the individual samples are printed instead of the 149 | statistics. The messages are printed at the LOG_INFO level. 150 | The default is 0 (disabled). 151 | .TP 152 | .B \-w 153 | Wait until ptp4l is in a synchronized state. If the 154 | .B \-O 155 | option is not used, also keep the offset between the slave and master 156 | times updated according to the currentUtcOffset value obtained from ptp4l and 157 | the direction of the clock synchronization. 158 | .TP 159 | .BI \-n " domain-number" 160 | Specify the domain number used by ptp4l. The default is 0. 161 | .TP 162 | .B \-x 163 | When a leap second is announced, don't apply it in the kernel by stepping the 164 | clock, but let the servo correct the one-second offset slowly by changing the 165 | clock frequency (unless the 166 | .B \-S 167 | option is used). 168 | .TP 169 | .BI \-l " print-level" 170 | Set the maximum syslog level of messages which should be printed or sent to 171 | the system logger. The default is 6 (LOG_INFO). 172 | .TP 173 | .B \-m 174 | Print messages to the standard output. 175 | .TP 176 | .B \-q 177 | Don't send messages to the system logger. 178 | .TP 179 | .BI \-h 180 | Display a help message. 181 | .TP 182 | .B \-v 183 | Prints the software version and exits. 184 | 185 | .SH TIME SCALE USAGE 186 | 187 | .B Ptp4l 188 | uses either PTP time scale or UTC (Coordinated Universal Time) time 189 | scale. PTP time scale is continuous and shifted against UTC by a few tens of 190 | seconds as PTP time scale does not apply leap seconds. 191 | 192 | In hardware time stamping mode, 193 | .B ptp4l 194 | announces use of PTP time scale and PHC 195 | is used for the stamps. That means PHC must follow PTP time scale while system 196 | clock follows UTC. Time offset between these two is maintained by 197 | .BR phc2sys . 198 | 199 | .B Phc2sys 200 | acquires the offset value either by reading it from ptp4l when 201 | .B \-w 202 | is in effect or from command line when 203 | .B \-O 204 | is supplied. Failure to maintain the correct offset can result in local system 205 | clock being off some seconds to domain master system clock when in slave mode, 206 | or incorect PTP time announced to the network in case the host is the domain 207 | master. 208 | 209 | .SH EXAMPLES 210 | 211 | The host is a domain master, PTP clock is synchronized to system clock and the 212 | time offset is obtained from 213 | .BR ptp4l . 214 | .B Phc2sys 215 | waits for 216 | .B ptp4l 217 | to get at least one port in master or slave mode before starting the 218 | synchronization. 219 | 220 | .RS 221 | \f(CWphc2sys \-c /dev/ptp0 \-s CLOCK_REALTIME \-w\fP 222 | .RE 223 | 224 | Same as above, time offset is provided on command line and 225 | .B phc2sys 226 | does not wait for 227 | .BR ptp4l . 228 | 229 | .RS 230 | \f(CWphc2sys \-c /dev/ptp0 \-s CLOCK_REALTIME \-O 35\fP 231 | .RE 232 | 233 | The host is in slave mode, system clock is synchronized from PTP clock, 234 | .B phc2sys 235 | waits for 236 | .B ptp4l 237 | and the offset is set automatically. 238 | 239 | .RS 240 | \f(CWphc2sys \-s /dev/ptp0 \-w\fP 241 | .RE 242 | 243 | Same as above, PTP clock id is read from the network interface, the offset is 244 | provided on command line 245 | .B phc2sys 246 | does not wait. 247 | 248 | .RS 249 | \f(CWphc2sys \-s eth0 \-O \-35\fP 250 | .RE 251 | 252 | .SH SEE ALSO 253 | .BR ptp4l (8) 254 | -------------------------------------------------------------------------------- /pi.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file pi.c 3 | * @brief Implements a Proportional Integral clock servo. 4 | * @note Copyright (C) 2011 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #include 21 | #include 22 | 23 | #include "pi.h" 24 | #include "print.h" 25 | #include "servo_private.h" 26 | 27 | #define HWTS_KP_SCALE 0.7 28 | #define HWTS_KI_SCALE 0.3 29 | #define SWTS_KP_SCALE 0.1 30 | #define SWTS_KI_SCALE 0.001 31 | 32 | #define MAX_KP_NORM_MAX 1.0 33 | #define MAX_KI_NORM_MAX 2.0 34 | 35 | #define FREQ_EST_MARGIN 0.001 36 | 37 | /* These take their values from the configuration file. (see ptp4l.c) */ 38 | double configured_pi_kp = 0.0; 39 | double configured_pi_ki = 0.0; 40 | double configured_pi_kp_scale = 0.0; 41 | double configured_pi_kp_exponent = -0.3; 42 | double configured_pi_kp_norm_max = 0.7; 43 | double configured_pi_ki_scale = 0.0; 44 | double configured_pi_ki_exponent = 0.4; 45 | double configured_pi_ki_norm_max = 0.3; 46 | 47 | struct pi_servo { 48 | struct servo servo; 49 | int64_t offset[2]; 50 | uint64_t local[2]; 51 | double drift; 52 | double kp; 53 | double ki; 54 | double last_freq; 55 | int count; 56 | }; 57 | 58 | static void pi_destroy(struct servo *servo) 59 | { 60 | struct pi_servo *s = container_of(servo, struct pi_servo, servo); 61 | free(s); 62 | } 63 | 64 | static double pi_sample(struct servo *servo, 65 | int64_t offset, 66 | uint64_t local_ts, 67 | enum servo_state *state) 68 | { 69 | struct pi_servo *s = container_of(servo, struct pi_servo, servo); 70 | double ki_term, ppb = s->last_freq; 71 | double freq_est_interval, localdiff; 72 | 73 | switch (s->count) { 74 | case 0: 75 | s->offset[0] = offset; 76 | s->local[0] = local_ts; 77 | *state = SERVO_UNLOCKED; 78 | s->count = 1; 79 | break; 80 | case 1: 81 | s->offset[1] = offset; 82 | s->local[1] = local_ts; 83 | 84 | /* Make sure the first sample is older than the second. */ 85 | if (s->local[0] >= s->local[1]) { 86 | *state = SERVO_UNLOCKED; 87 | s->count = 0; 88 | break; 89 | } 90 | 91 | /* Wait long enough before estimating the frequency offset. */ 92 | localdiff = (s->local[1] - s->local[0]) / 1e9; 93 | localdiff += localdiff * FREQ_EST_MARGIN; 94 | freq_est_interval = 0.016 / s->ki; 95 | if (freq_est_interval > 1000.0) { 96 | freq_est_interval = 1000.0; 97 | } 98 | if (localdiff < freq_est_interval) { 99 | *state = SERVO_UNLOCKED; 100 | break; 101 | } 102 | 103 | /* Adjust drift by the measured frequency offset. */ 104 | s->drift += (1e9 - s->drift) * (s->offset[1] - s->offset[0]) / 105 | (s->local[1] - s->local[0]); 106 | 107 | if (s->drift < -servo->max_frequency) 108 | s->drift = -servo->max_frequency; 109 | else if (s->drift > servo->max_frequency) 110 | s->drift = servo->max_frequency; 111 | 112 | if ((servo->first_update && 113 | servo->first_step_threshold && 114 | servo->first_step_threshold < fabs(offset)) || 115 | (servo->step_threshold && 116 | servo->step_threshold < fabs(offset))) 117 | *state = SERVO_JUMP; 118 | else 119 | *state = SERVO_LOCKED; 120 | 121 | ppb = s->drift; 122 | s->count = 2; 123 | break; 124 | case 2: 125 | /* 126 | * reset the clock servo when offset is greater than the max 127 | * offset value. Note that the clock jump will be performed in 128 | * step 1, so it is not necessary to have clock jump 129 | * immediately. This allows re-calculating drift as in initial 130 | * clock startup. 131 | */ 132 | if (servo->step_threshold && 133 | servo->step_threshold < fabs(offset)) { 134 | *state = SERVO_UNLOCKED; 135 | s->count = 0; 136 | break; 137 | } 138 | 139 | ki_term = s->ki * offset; 140 | ppb = s->kp * offset + s->drift + ki_term; 141 | if (ppb < -servo->max_frequency) { 142 | ppb = -servo->max_frequency; 143 | } else if (ppb > servo->max_frequency) { 144 | ppb = servo->max_frequency; 145 | } else { 146 | s->drift += ki_term; 147 | } 148 | *state = SERVO_LOCKED; 149 | break; 150 | } 151 | 152 | s->last_freq = ppb; 153 | return ppb; 154 | } 155 | 156 | static void pi_sync_interval(struct servo *servo, double interval) 157 | { 158 | struct pi_servo *s = container_of(servo, struct pi_servo, servo); 159 | 160 | s->kp = configured_pi_kp_scale * pow(interval, configured_pi_kp_exponent); 161 | if (s->kp > configured_pi_kp_norm_max / interval) 162 | s->kp = configured_pi_kp_norm_max / interval; 163 | 164 | s->ki = configured_pi_ki_scale * pow(interval, configured_pi_ki_exponent); 165 | if (s->ki > configured_pi_ki_norm_max / interval) 166 | s->ki = configured_pi_ki_norm_max / interval; 167 | 168 | pr_debug("PI servo: sync interval %.3f kp %.3f ki %.6f", 169 | interval, s->kp, s->ki); 170 | } 171 | 172 | static void pi_reset(struct servo *servo) 173 | { 174 | struct pi_servo *s = container_of(servo, struct pi_servo, servo); 175 | 176 | s->count = 0; 177 | } 178 | 179 | struct servo *pi_servo_create(int fadj, int sw_ts) 180 | { 181 | struct pi_servo *s; 182 | 183 | s = calloc(1, sizeof(*s)); 184 | if (!s) 185 | return NULL; 186 | 187 | s->servo.destroy = pi_destroy; 188 | s->servo.sample = pi_sample; 189 | s->servo.sync_interval = pi_sync_interval; 190 | s->servo.reset = pi_reset; 191 | s->drift = fadj; 192 | s->last_freq = fadj; 193 | s->kp = 0.0; 194 | s->ki = 0.0; 195 | 196 | if (configured_pi_kp && configured_pi_ki) { 197 | /* Use the constants as configured by the user without 198 | adjusting for sync interval unless they make the servo 199 | unstable. */ 200 | configured_pi_kp_scale = configured_pi_kp; 201 | configured_pi_ki_scale = configured_pi_ki; 202 | configured_pi_kp_exponent = 0.0; 203 | configured_pi_ki_exponent = 0.0; 204 | configured_pi_kp_norm_max = MAX_KP_NORM_MAX; 205 | configured_pi_ki_norm_max = MAX_KI_NORM_MAX; 206 | } else if (!configured_pi_kp_scale || !configured_pi_ki_scale) { 207 | if (sw_ts) { 208 | configured_pi_kp_scale = SWTS_KP_SCALE; 209 | configured_pi_ki_scale = SWTS_KI_SCALE; 210 | } else { 211 | configured_pi_kp_scale = HWTS_KP_SCALE; 212 | configured_pi_ki_scale = HWTS_KI_SCALE; 213 | } 214 | } 215 | 216 | return &s->servo; 217 | } 218 | -------------------------------------------------------------------------------- /pi.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file pi.h 3 | * @note Copyright (C) 2011 Richard Cochran 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #ifndef HAVE_PI_H 20 | #define HAVE_PI_H 21 | 22 | #include "servo.h" 23 | 24 | /** 25 | * When set to a non-zero value, this variable determines the 26 | * proportional constant for the PI controller. 27 | */ 28 | extern double configured_pi_kp; 29 | 30 | /** 31 | * When set to a non-zero value, this variable determines the 32 | * integral constant for the PI controller. 33 | */ 34 | extern double configured_pi_ki; 35 | 36 | /** 37 | * When set to a non-zero value, this variable determines the scale in the 38 | * formula used to set the proportional constant of the PI controller from the 39 | * sync interval. 40 | * kp = min(kp_scale * sync^kp_exponent, kp_norm_max / sync) 41 | */ 42 | extern double configured_pi_kp_scale; 43 | 44 | /** 45 | * This variable determines the exponent in the formula used to set the 46 | * proportional constant of the PI controller from the sync interval. 47 | * kp = min(kp_scale * sync^kp_exponent, kp_norm_max / sync) 48 | */ 49 | extern double configured_pi_kp_exponent; 50 | 51 | /** 52 | * This variable determines the normalized maximum in the formula used to set 53 | * the proportional constant of the PI controller from the sync interval. 54 | * kp = min(kp_scale * sync^kp_exponent, kp_norm_max / sync) 55 | */ 56 | extern double configured_pi_kp_norm_max; 57 | 58 | /** 59 | * When set to a non-zero value, this variable determines the scale in the 60 | * formula used to set the integral constant of the PI controller from the 61 | * sync interval. 62 | * ki = min(ki_scale * sync^ki_exponent, ki_norm_max / sync) 63 | */ 64 | extern double configured_pi_ki_scale; 65 | 66 | /** 67 | * This variable determines the exponent in the formula used to set the 68 | * integral constant of the PI controller from the sync interval. 69 | * ki = min(ki_scale * sync^ki_exponent, ki_norm_max / sync) 70 | */ 71 | extern double configured_pi_ki_exponent; 72 | 73 | /** 74 | * This variable determines the normalized maximum in the formula used to set 75 | * the integral constant of the PI controller from the sync interval. 76 | * ki = min(ki_scale * sync^ki_exponent, ki_norm_max / sync) 77 | */ 78 | extern double configured_pi_ki_norm_max; 79 | 80 | struct servo *pi_servo_create(int fadj, int sw_ts); 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /pmc.8: -------------------------------------------------------------------------------- 1 | .TH PMC 8 "October 2013" "linuxptp" 2 | .SH NAME 3 | pmc \- PTP management client 4 | 5 | .SH SYNOPSIS 6 | .B pmc 7 | [ 8 | .B \-2 9 | | 10 | .B \-4 11 | | 12 | .B \-6 13 | | 14 | .B \-u 15 | ] [ 16 | .BI \-b " boundary-hops" 17 | ] [ 18 | .BI \-d " domain-number" 19 | ] [ 20 | .BI \-i " interface" 21 | ] [ 22 | .BI \-s " uds-address" 23 | ] [ 24 | .BI \-t " transport-specific-field" 25 | ] [ 26 | .B \-v 27 | ] [ 28 | .B \-z 29 | ] [ command ] ... 30 | 31 | .SH DESCRIPTION 32 | .B pmc 33 | is a program which implements a PTP management client according to IEEE 34 | standard 1588. The program reads from the standard input or from the command 35 | line actions specified by name and management ID, sends them over the selected 36 | transport and prints any received replies. There are three actions supported: 37 | .B GET 38 | retrieves the specified information, 39 | .B SET 40 | updates the specified information and 41 | .B CMD 42 | (or 43 | .BR COMMAND ) 44 | initiates the specified event. 45 | 46 | By default the management commands are addressed to all ports. The 47 | .B TARGET 48 | command can be used to select a particular clock and port for the 49 | subsequent messages. 50 | 51 | Command 52 | .B help 53 | can be used to get a list of supported actions and management IDs. 54 | 55 | .SH OPTIONS 56 | .TP 57 | .B \-2 58 | Select the IEEE 802.3 network transport. 59 | .TP 60 | .B \-4 61 | Select the UDP IPv4 network transport. This is the default transport. 62 | .TP 63 | .B \-6 64 | Select the UDP IPv6 network transport. 65 | .TP 66 | .B \-u 67 | Select the Unix Domain Socket transport. 68 | .TP 69 | .BI \-b " boundary-hops" 70 | Specify the boundary hops value in sent messages. The default is 1. 71 | .TP 72 | .BI \-d " domain-number" 73 | Specify the domain number in sent messages. The default is 0. 74 | .TP 75 | .BI \-i " interface" 76 | Specify the network interface. The default is /var/run/pmc for the Unix Domain 77 | Socket transport and eth0 for the other transports. 78 | .TP 79 | .BI \-s " uds-address" 80 | Specifies the address of the server's UNIX domain socket. 81 | The default is /var/run/ptp4l. 82 | .TP 83 | .BI \-t " transport-specific-field" 84 | Specify the transport specific field in sent messages as a hexadecimal number. 85 | The default is 0x0. 86 | .TP 87 | .B \-h 88 | Display a help message. 89 | .TP 90 | .B \-v 91 | Prints the software version and exits. 92 | .TP 93 | .B \-z 94 | The official interpretation of the 1588 standard mandates sending 95 | GET actions with valid (but meaningless) TLV values. Therefore the 96 | pmc program normally sends GET requests with properly formed TLV 97 | values. This option enables the legacy option of sending zero 98 | length TLV values instead. 99 | 100 | .SH MANAGEMENT IDS 101 | 102 | .TP 103 | .B ANNOUNCE_RECEIPT_TIMEOUT 104 | .TP 105 | .B CLOCK_ACCURACY 106 | .TP 107 | .B CLOCK_DESCRIPTION 108 | .TP 109 | .B CURRENT_DATA_SET 110 | .TP 111 | .B DEFAULT_DATA_SET 112 | .TP 113 | .B DELAY_MECHANISM 114 | .TP 115 | .B DOMAIN 116 | .TP 117 | .B GRANDMASTER_SETTINGS_NP 118 | .TP 119 | .B LOG_ANNOUNCE_INTERVAL 120 | .TP 121 | .B LOG_MIN_PDELAY_REQ_INTERVAL 122 | .TP 123 | .B LOG_SYNC_INTERVAL 124 | .TP 125 | .B NULL_MANAGEMENT 126 | .TP 127 | .B PARENT_DATA_SET 128 | .TP 129 | .B PORT_DATA_SET 130 | .TP 131 | .B PORT_DATA_SET_NP 132 | .TP 133 | .B PRIORITY1 134 | .TP 135 | .B PRIORITY2 136 | .TP 137 | .B SLAVE_ONLY 138 | .TP 139 | .B TIMESCALE_PROPERTIES 140 | .TP 141 | .B TIME_PROPERTIES_DATA_SET 142 | .TP 143 | .B TIME_STATUS_NP 144 | .TP 145 | .B TRACEABILITY_PROPERTIES 146 | .TP 147 | .B USER_DESCRIPTION 148 | .TP 149 | .B VERSION_NUMBER 150 | 151 | .SH SEE ALSO 152 | .BR ptp4l (8) 153 | -------------------------------------------------------------------------------- /pmc_common.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file pmc_common.h 3 | * @brief Code shared between PTP management clients. 4 | * @note Copyright (C) 2013 Miroslav Lichvar 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | #ifndef HAVE_PMC_COMMON_H 22 | #define HAVE_PMC_COMMON_H 23 | 24 | #include "msg.h" 25 | #include "transport.h" 26 | 27 | struct pmc; 28 | 29 | struct pmc *pmc_create(enum transport_type transport_type, const char *iface_name, 30 | UInteger8 boundary_hops, UInteger8 domain_number, 31 | UInteger8 transport_specific, int zero_datalen); 32 | 33 | void pmc_destroy(struct pmc *pmc); 34 | 35 | int pmc_get_transport_fd(struct pmc *pmc); 36 | 37 | int pmc_send_get_action(struct pmc *pmc, int id); 38 | 39 | int pmc_send_set_action(struct pmc *pmc, int id, void *data, int datasize); 40 | 41 | struct ptp_message *pmc_recv(struct pmc *pmc); 42 | 43 | int pmc_target(struct pmc *pmc, struct PortIdentity *pid); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /print.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file print.c 3 | * @note Copyright (C) 2011 Richard Cochran 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "print.h" 26 | 27 | static int verbose = 0; 28 | static int print_level = LOG_INFO; 29 | static int use_syslog = 1; 30 | static const char *progname; 31 | 32 | void print_set_progname(const char *name) 33 | { 34 | progname = name; 35 | } 36 | 37 | void print_set_syslog(int value) 38 | { 39 | use_syslog = value ? 1 : 0; 40 | } 41 | 42 | void print_set_level(int level) 43 | { 44 | print_level = level; 45 | } 46 | 47 | void print_set_verbose(int value) 48 | { 49 | verbose = value ? 1 : 0; 50 | } 51 | 52 | void print(int level, char const *format, ...) 53 | { 54 | struct timespec ts; 55 | va_list ap; 56 | char buf[1024]; 57 | FILE *f; 58 | 59 | if (level > print_level) 60 | return; 61 | 62 | clock_gettime(CLOCK_MONOTONIC, &ts); 63 | 64 | va_start(ap, format); 65 | vsnprintf(buf, sizeof(buf), format, ap); 66 | va_end(ap); 67 | 68 | if (verbose) { 69 | f = level >= LOG_NOTICE ? stdout : stderr; 70 | fprintf(f, "%s[%ld.%03ld]: %s\n", 71 | progname ? progname : "", 72 | ts.tv_sec, ts.tv_nsec / 1000000, buf); 73 | fflush(f); 74 | } 75 | if (use_syslog) { 76 | syslog(level, "[%ld.%03ld] %s", 77 | ts.tv_sec, ts.tv_nsec / 1000000, buf); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /print.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file print.h 3 | * @brief Logging support functions 4 | * @note Copyright (C) 2011 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_PRINT_H 21 | #define HAVE_PRINT_H 22 | 23 | #include 24 | 25 | #define PRINT_LEVEL_MIN LOG_EMERG 26 | #define PRINT_LEVEL_MAX LOG_DEBUG 27 | 28 | #ifdef __GNUC__ 29 | __attribute__ ((format (printf, 2, 3))) 30 | #endif 31 | void print(int level, char const *format, ...); 32 | 33 | void print_set_progname(const char *name); 34 | void print_set_syslog(int value); 35 | void print_set_level(int level); 36 | void print_set_verbose(int value); 37 | 38 | #define pr_emerg(x...) print(LOG_EMERG, x) 39 | #define pr_alert(x...) print(LOG_ALERT, x) 40 | #define pr_crit(x...) print(LOG_CRIT, x) 41 | #define pr_err(x...) print(LOG_ERR, x) 42 | #define pr_warning(x...) print(LOG_WARNING, x) 43 | #define pr_notice(x...) print(LOG_NOTICE, x) 44 | #define pr_info(x...) print(LOG_INFO, x) 45 | #define pr_debug(x...) print(LOG_DEBUG, x) 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /ptp4l_asi.c: -------------------------------------------------------------------------------- 1 | 2 | #define _GNU_SOURCE 3 | #include 4 | #include 5 | 6 | #include "libmain.h" 7 | #include "clock.h" 8 | #include "util.h" 9 | 10 | #include 11 | #include 12 | 13 | struct pub_obj { 14 | struct ubus_object obj; 15 | struct ubus_object_type type; 16 | struct ubus_method methods[2]; 17 | void *priv; 18 | }; 19 | 20 | static struct blob_buf b; 21 | static struct port_capable_info as_capable_info[MAX_PORTS]; 22 | 23 | static struct app_state { 24 | struct ubus_context *ubus_ctx; 25 | struct clock *c; 26 | int num_obj; 27 | struct pub_obj *objs; 28 | } s; 29 | 30 | void ubus_connection_lost(struct ubus_context *ctx) 31 | { 32 | ctx->sock.fd = -1; 33 | clock_register_port_update_cb(s.c, NULL, NULL); 34 | clock_register_clock_update_cb(s.c, NULL, NULL); 35 | fprintf(stderr, "Disconnected from ubusd\n"); 36 | } 37 | 38 | #define BOOL_AS_STRING(val) (val?"true":"false") 39 | #define INFO_FIELD_UNPACK(ptr, fn) ptr->fn.actual,ptr->fn.expected, BOOL_AS_STRING(ptr->fn.unmet) 40 | #define INFO_FIELD_UNPACK_BOOL(ptr, fn) BOOL_AS_STRING(ptr->fn.actual),BOOL_AS_STRING(ptr->fn.expected), BOOL_AS_STRING(ptr->fn.unmet) 41 | 42 | struct blob_attr *as_info_payload(struct port_capable_info *info) 43 | { 44 | struct timespec ts; 45 | char *msg = NULL; 46 | clock_gettime(CLOCK_MONOTONIC, &ts); 47 | uint64_t ns = ((uint64_t)ts.tv_sec)*NS_PER_SEC+ts.tv_nsec; 48 | blob_buf_init(&b, 0); 49 | asprintf(&msg, "{\"tm\":%llu,\"port-num\":%d,\"as-capable\":%s," 50 | "\"max-peer-delay\":[%lld,%lld,%s]," 51 | "\"min-peer-delay\":[%lld,%lld,%s]," 52 | "\"max-missed-pdr\":[%hhu,%hhu,%s]," 53 | "\"max-multiple-seq-pdr\":[%hhu,%hhu,%s]," 54 | "\"peer-port-id-valid\":[%s,%s,%s]," 55 | "\"nrate-ratio-valid\":[%s,%s,%s]," 56 | "\"port-state-acceptable\":[%s,%s,%s]}", ns, info->port_num, BOOL_AS_STRING(info->as_capable), 57 | INFO_FIELD_UNPACK(info, max_peer_delay), 58 | INFO_FIELD_UNPACK(info, min_peer_delay), 59 | INFO_FIELD_UNPACK(info, max_missed_pdr), 60 | INFO_FIELD_UNPACK(info, max_multiple_seq_pdr), 61 | INFO_FIELD_UNPACK_BOOL(info, peer_port_id_valid), 62 | INFO_FIELD_UNPACK_BOOL(info, nrate_ratio_valid), 63 | INFO_FIELD_UNPACK_BOOL(info, port_state_acceptable)); 64 | blob_put(&b, BLOB_ATTR_STRING, msg, strlen(msg)); 65 | free(msg); 66 | return blob_data(b.head); 67 | } 68 | 69 | int ascapable_ubus_method_handler(struct ubus_context *ctx, struct ubus_object *obj, 70 | struct ubus_request_data *req, 71 | const char *method, struct blob_attr *msg) 72 | { 73 | struct pub_obj *p = container_of(obj, struct pub_obj, obj); 74 | int port_num = (int)p->priv; 75 | struct port_capable_info *info = &as_capable_info[port_num-1]; 76 | if (port_num != info->port_num) 77 | return UBUS_STATUS_NO_DATA; 78 | struct blob_attr *payload = as_info_payload(info); 79 | ubus_send_reply(ctx, req, payload); 80 | return 0; 81 | } 82 | 83 | struct blob_attr *parent_ds_payload(struct parentDS *pds) 84 | { 85 | struct timespec ts; 86 | char *msg = NULL; 87 | clock_gettime(CLOCK_MONOTONIC, &ts); 88 | uint64_t ns = ((uint64_t)ts.tv_sec)*NS_PER_SEC+ts.tv_nsec; 89 | blob_buf_init(&b, 0); 90 | asprintf(&msg, "{\"tm\":%llu,\"parent-clock-identity\":\"%s\"}", 91 | ns, cid2str(&pds->grandmasterIdentity)); 92 | blob_put(&b, BLOB_ATTR_STRING, msg, strlen(msg)); 93 | free(msg); 94 | return blob_data(b.head); 95 | } 96 | 97 | int parentds_ubus_method_handler(struct ubus_context *ctx, struct ubus_object *obj, 98 | struct ubus_request_data *req, 99 | const char *method, struct blob_attr *msg) 100 | { 101 | uint8_t null_clk[] = {0,0,0,0,0,0,0,0}; 102 | struct pub_obj *p = container_of(obj, struct pub_obj, obj); 103 | struct clock *c = (struct clock *)p->priv; 104 | struct parentDS *pds = &clock_parent_ds(c)->pds; 105 | if (!memcmp(&null_clk, &pds->grandmasterIdentity, sizeof(pds->grandmasterIdentity))) 106 | return UBUS_STATUS_NO_DATA; 107 | struct blob_attr *payload = parent_ds_payload(pds); 108 | ubus_send_reply(ctx, req, payload); 109 | return 0; 110 | } 111 | 112 | void clock_update_cb(int data_id, int ev_id, void *buf, size_t buf_len, void *priv) 113 | { 114 | if (!buf || data_id != PARENT_DATA_SET) 115 | return; 116 | assert(s.ubus_ctx); 117 | assert(s.ubus_ctx->sock.fd >= 0); 118 | struct parentDS *pds = (struct parentDS *)buf; 119 | struct blob_attr *payload = parent_ds_payload(pds); 120 | ubus_notify(s.ubus_ctx, &s.objs[s.num_obj-1].obj, "update", payload, -1); 121 | } 122 | 123 | void port_update_cb(struct port_capable_info *info, void *priv) 124 | { 125 | if (!info || !info->port_num) 126 | return; 127 | assert(s.ubus_ctx); 128 | assert(s.ubus_ctx->sock.fd >= 0); 129 | as_capable_info[info->port_num-1] = *info; 130 | struct blob_attr *payload = as_info_payload(info); 131 | ubus_notify(s.ubus_ctx, &s.objs[info->port_num-1].obj, "update", payload, -1); 132 | } 133 | 134 | void setup_ubus_objects(void) 135 | { 136 | int idx; 137 | 138 | int num_ports = clock_num_ports(s.c); 139 | s.num_obj = num_ports+1; 140 | s.objs = calloc(sizeof(struct pub_obj), s.num_obj); 141 | for (idx = 0; idx < num_ports; idx++) { 142 | struct ubus_object *o = &s.objs[idx].obj; 143 | struct ubus_method *m = s.objs[idx].methods; 144 | struct ubus_object_type *t = &s.objs[idx].type; 145 | char *name = NULL; 146 | asprintf(&name, "0:vol-st/avb/gptp/port/%d/as-capable", idx+1); 147 | s.objs[idx].priv = (void*)idx+1; 148 | o->name = name; 149 | o->type = t; 150 | m[0].name = NULL; 151 | m[0].handler = ascapable_ubus_method_handler; 152 | m[1].name = "*"; 153 | m[1].handler = NULL; 154 | o->n_methods = 1; 155 | o->methods = &m[0]; 156 | t->n_methods = 1; 157 | t->methods = &m[1]; 158 | int ret = ubus_add_object(s.ubus_ctx, o); 159 | if (ret) { 160 | fprintf(stderr, "Cannot add object for port %d, error %d\n", idx, ret); 161 | exit(1); 162 | } 163 | } 164 | { 165 | idx = s.num_obj-1; 166 | struct ubus_object *o = &s.objs[idx].obj; 167 | struct ubus_method *m = s.objs[idx].methods; 168 | struct ubus_object_type *t = &s.objs[idx].type; 169 | char *name = NULL; 170 | asprintf(&name, "0:vol-st/avb/gptp/parent-ds"); 171 | s.objs[idx].priv = (void*)s.c; 172 | o->name = name; 173 | o->type = t; 174 | m[0].name = NULL; 175 | m[0].handler = parentds_ubus_method_handler; 176 | m[1].name = "*"; 177 | m[1].handler = NULL; 178 | o->n_methods = 1; 179 | o->methods = &m[0]; 180 | t->n_methods = 1; 181 | t->methods = &m[1]; 182 | int ret = ubus_add_object(s.ubus_ctx, o); 183 | if (ret) { 184 | fprintf(stderr, "Cannot add parent-ds object, error %d\n", ret); 185 | exit(1); 186 | } 187 | } 188 | } 189 | 190 | int main(int argc, char *argv[]) 191 | { 192 | char *config_file = NULL; 193 | 194 | if (argc > 1) 195 | config_file = argv[1]; 196 | 197 | s.c = ptp4l_setup(config_file); 198 | if (!s.c) { 199 | fprintf(stderr, "Configuration error\n"); 200 | exit(1); 201 | } 202 | 203 | while (true) { 204 | int ret = clock_poll(s.c); 205 | if (ret) 206 | break; 207 | 208 | if (!s.ubus_ctx) { 209 | s.ubus_ctx = ubus_connect(NULL); 210 | if (!s.ubus_ctx) 211 | continue; 212 | s.ubus_ctx->connection_lost = ubus_connection_lost; 213 | setup_ubus_objects(); 214 | clock_register_port_update_cb(s.c, port_update_cb, NULL); 215 | clock_register_clock_update_cb(s.c, clock_update_cb, NULL); 216 | fprintf(stderr, "Connected to ubusd\n"); 217 | } 218 | 219 | if (s.ubus_ctx->sock.fd < 0) { 220 | if (!ubus_reconnect(s.ubus_ctx, NULL)) { 221 | clock_register_port_update_cb(s.c, port_update_cb, NULL); 222 | clock_register_clock_update_cb(s.c, clock_update_cb, NULL); 223 | fprintf(stderr, "Connected to ubusd\n"); 224 | } 225 | } else { 226 | ubus_handle_event(s.ubus_ctx); 227 | /* process pending messages */ 228 | s.ubus_ctx->pending_timer.cb(&s.ubus_ctx->pending_timer); 229 | } 230 | } 231 | ptp4l_destroy(s.c); 232 | ubus_free(s.ubus_ctx); 233 | return 0; 234 | } 235 | -------------------------------------------------------------------------------- /raw.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file raw.h 3 | * @brief Implements transport over IEEE 802.3 aka raw Ethernet. 4 | * @note Copyright (C) 2012 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_RAW_H 21 | #define HAVE_RAW_H 22 | 23 | #include "fd.h" 24 | #include "transport.h" 25 | 26 | /** 27 | * The MAC address to use for non-peer messages. 28 | */ 29 | extern unsigned char ptp_dst_mac[]; 30 | 31 | /** 32 | * The MAC address to use for peer messages. 33 | */ 34 | extern unsigned char p2p_dst_mac[]; 35 | 36 | /** 37 | * Allocate an instance of a raw Ethernet transport. 38 | * @return Pointer to a new transport instance on success, NULL otherwise. 39 | */ 40 | struct transport *raw_transport_create(void); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /servo.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file servo.c 3 | * @note Copyright (C) 2011 Richard Cochran 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include 20 | 21 | #include "linreg.h" 22 | #include "pi.h" 23 | #include "servo_private.h" 24 | 25 | #define NSEC_PER_SEC 1000000000 26 | 27 | double servo_step_threshold = 0.0; 28 | double servo_first_step_threshold = 0.00002; /* 20 microseconds */ 29 | int servo_max_frequency = 900000000; 30 | 31 | struct servo *servo_create(enum servo_type type, int fadj, int max_ppb, int sw_ts) 32 | { 33 | struct servo *servo; 34 | 35 | switch (type) { 36 | case CLOCK_SERVO_PI: 37 | servo = pi_servo_create(fadj, sw_ts); 38 | break; 39 | case CLOCK_SERVO_LINREG: 40 | servo = linreg_servo_create(fadj); 41 | break; 42 | default: 43 | return NULL; 44 | } 45 | 46 | if (servo_step_threshold > 0.0) { 47 | servo->step_threshold = servo_step_threshold * NSEC_PER_SEC; 48 | } else { 49 | servo->step_threshold = 0.0; 50 | } 51 | 52 | if (servo_first_step_threshold > 0.0) { 53 | servo->first_step_threshold = 54 | servo_first_step_threshold * NSEC_PER_SEC; 55 | } else { 56 | servo->first_step_threshold = 0.0; 57 | } 58 | 59 | servo->max_frequency = max_ppb; 60 | if (servo_max_frequency && servo->max_frequency > servo_max_frequency) { 61 | servo->max_frequency = servo_max_frequency; 62 | } 63 | 64 | servo->first_update = 1; 65 | 66 | return servo; 67 | } 68 | 69 | void servo_destroy(struct servo *servo) 70 | { 71 | servo->destroy(servo); 72 | } 73 | 74 | double servo_sample(struct servo *servo, 75 | int64_t offset, 76 | uint64_t local_ts, 77 | enum servo_state *state) 78 | { 79 | double r; 80 | 81 | r = servo->sample(servo, offset, local_ts, state); 82 | 83 | if (*state != SERVO_UNLOCKED) 84 | servo->first_update = 0; 85 | 86 | return r; 87 | } 88 | 89 | void servo_sync_interval(struct servo *servo, double interval) 90 | { 91 | servo->sync_interval(servo, interval); 92 | } 93 | 94 | void servo_reset(struct servo *servo) 95 | { 96 | servo->reset(servo); 97 | } 98 | 99 | double servo_rate_ratio(struct servo *servo) 100 | { 101 | if (servo->rate_ratio) 102 | return servo->rate_ratio(servo); 103 | 104 | return 1.0; 105 | } 106 | -------------------------------------------------------------------------------- /servo.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file servo.h 3 | * @brief Implements a generic clock servo interface. 4 | * @note Copyright (C) 2011 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_SERVO_H 21 | #define HAVE_SERVO_H 22 | 23 | #include 24 | 25 | /** 26 | * When set to a non-zero value, this variable controls the maximum allowed 27 | * offset before a clock jump occurs instead of the default clock-slewing 28 | * mechanism. 29 | * 30 | * Note that this variable is measured in seconds, and allows fractional values. 31 | */ 32 | extern double servo_step_threshold; 33 | 34 | /** 35 | * When set to zero, the clock is not stepped on start. When set to a non-zero 36 | * value, the value bahaves as a threshold and the clock is stepped on start if 37 | * the offset is bigger than the threshold. 38 | * 39 | * Note that this variable is measured in seconds, and allows fractional values. 40 | */ 41 | extern double servo_first_step_threshold; 42 | 43 | /** 44 | * When set to a non-zero value, this variable sets an additional limit for 45 | * the frequency adjustment of the clock. It's in ppb. 46 | */ 47 | extern int servo_max_frequency; 48 | 49 | /** Opaque type */ 50 | struct servo; 51 | 52 | /** 53 | * Defines the available servo cores 54 | */ 55 | enum servo_type { 56 | CLOCK_SERVO_PI, 57 | CLOCK_SERVO_LINREG, 58 | }; 59 | 60 | /** 61 | * Defines the caller visible states of a clock servo. 62 | */ 63 | enum servo_state { 64 | 65 | /** 66 | * The servo is not yet ready to track the master clock. 67 | */ 68 | SERVO_UNLOCKED, 69 | 70 | /** 71 | * The is ready to track and requests a clock jump to 72 | * immediately correct the estimated offset. 73 | */ 74 | SERVO_JUMP, 75 | 76 | /** 77 | * The servo is tracking the master clock. 78 | */ 79 | SERVO_LOCKED, 80 | }; 81 | 82 | /** 83 | * Create a new instance of a clock servo. 84 | * @param type The type of the servo to create. 85 | * @param fadj The clock's current adjustment in parts per billion. 86 | * @param max_ppb The absolute maxinum adjustment allowed by the clock 87 | * in parts per billion. The clock servo will clamp its 88 | * output according to this limit. 89 | * @param sw_ts Indicates that software time stamping will be used, 90 | * and the servo should use more aggressive filtering. 91 | * @return A pointer to a new servo on success, NULL otherwise. 92 | */ 93 | struct servo *servo_create(enum servo_type type, int fadj, int max_ppb, int sw_ts); 94 | 95 | /** 96 | * Destroy an instance of a clock servo. 97 | * @param servo Pointer to a servo obtained via @ref servo_create(). 98 | */ 99 | void servo_destroy(struct servo *servo); 100 | 101 | /** 102 | * Feed a sample into a clock servo. 103 | * @param servo Pointer to a servo obtained via @ref servo_create(). 104 | * @param offset The estimated clock offset in nanoseconds. 105 | * @param local_ts The local time stamp of the sample in nanoseconds. 106 | * @param state Returns the servo's state. 107 | * @return The clock adjustment in parts per billion. 108 | */ 109 | double servo_sample(struct servo *servo, 110 | int64_t offset, 111 | uint64_t local_ts, 112 | enum servo_state *state); 113 | 114 | /** 115 | * Inform a clock servo about the master's sync interval. 116 | * @param servo Pointer to a servo obtained via @ref servo_create(). 117 | * @param interval The sync interval in seconds. 118 | */ 119 | void servo_sync_interval(struct servo *servo, double interval); 120 | 121 | /** 122 | * Reset a clock servo. 123 | * @param servo Pointer to a servo obtained via @ref servo_create(). 124 | */ 125 | void servo_reset(struct servo *servo); 126 | 127 | /** 128 | * Obtain ratio between master's frequency and current servo frequency. 129 | * @param servo Pointer to a servo obtained via @ref servo_create(). 130 | * @return The rate ratio, 1.0 is returned when not known. 131 | */ 132 | double servo_rate_ratio(struct servo *servo); 133 | 134 | #endif 135 | -------------------------------------------------------------------------------- /servo_private.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file servo_private.h 3 | * @note Copyright (C) 2011 Richard Cochran 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #ifndef HAVE_SERVO_PRIVATE_H 20 | #define HAVE_SERVO_PRIVATE_H 21 | 22 | #include "contain.h" 23 | 24 | struct servo { 25 | double max_frequency; 26 | double step_threshold; 27 | double first_step_threshold; 28 | int first_update; 29 | 30 | void (*destroy)(struct servo *servo); 31 | 32 | double (*sample)(struct servo *servo, 33 | int64_t offset, uint64_t local_ts, 34 | enum servo_state *state); 35 | 36 | void (*sync_interval)(struct servo *servo, double interval); 37 | 38 | void (*reset)(struct servo *servo); 39 | 40 | double (*rate_ratio)(struct servo *servo); 41 | }; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /sk.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file sk.h 3 | * @brief Implements protocol independent socket methods. 4 | * @note Copyright (C) 2012 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_SK_H 21 | #define HAVE_SK_H 22 | 23 | #include "address.h" 24 | #include "transport.h" 25 | 26 | /** 27 | * Contains timestamping information returned by the GET_TS_INFO ioctl. 28 | * @valid: set to non-zero when the info struct contains valid data. 29 | * @phc_index: index of the PHC device. 30 | * @so_timestamping: supported time stamping modes. 31 | * @tx_types: driver level transmit options for the HWTSTAMP ioctl. 32 | * @rx_filters: driver level receive options for the HWTSTAMP ioctl. 33 | */ 34 | struct sk_ts_info { 35 | int valid; 36 | int phc_index; 37 | unsigned int so_timestamping; 38 | unsigned int tx_types; 39 | unsigned int rx_filters; 40 | }; 41 | 42 | /** 43 | * Obtain the numerical index from a network interface by name. 44 | * @param fd An open socket. 45 | * @param device The name of the network interface of interest. 46 | * @return The result from the SIOCGIFINDEX ioctl. 47 | */ 48 | int sk_interface_index(int fd, const char *device); 49 | 50 | /** 51 | * Prepare a given socket for PTP "general" messages. 52 | * @param fd An open socket. 53 | * @return Zero on success, non-zero otherwise. 54 | */ 55 | int sk_general_init(int fd); 56 | 57 | /** 58 | * Obtain supported timestamping information 59 | * @param name The name of the interface 60 | * @param info Struct containing obtained timestamping information. 61 | * @return zero on success, negative on failure. 62 | */ 63 | int sk_get_ts_info(const char *name, struct sk_ts_info *sk_info); 64 | 65 | /** 66 | * Obtain the MAC address of a network interface. 67 | * @param name The name of the interface 68 | * @param mac Buffer to hold the result 69 | * @return Zero on success, non-zero otherwise. 70 | */ 71 | int sk_interface_macaddr(const char *name, struct address *mac); 72 | 73 | /** 74 | * Obtains the first IP address assigned to a network interface. 75 | * @param name The name of the interface 76 | * @param family The family of the address to get: AF_INET or AF_INET6 77 | * @param addr Buffer to hold the result 78 | * @return The number of bytes written to addr on success, -1 otherwise. 79 | */ 80 | int sk_interface_addr(const char *name, int family, struct address *addr); 81 | 82 | /** 83 | * Read a message from a socket. 84 | * @param fd An open socket. 85 | * @param buf Buffer to receive the message. 86 | * @param buflen Size of 'buf' in bytes. 87 | * @param addr Pointer to a buffer to receive the message's source 88 | * address. May be NULL. 89 | * @param hwts Pointer to a buffer to receive the message's time stamp. 90 | * @param flags Flags to pass to RECV(2). 91 | * @return 92 | */ 93 | int sk_receive(int fd, void *buf, int buflen, 94 | struct address *addr, struct hw_timestamp *hwts, int flags); 95 | 96 | /** 97 | * Enable time stamping on a given network interface. 98 | * @param fd An open socket. 99 | * @param device The name of the network interface to configure. 100 | * @param type The requested flavor of time stamping. 101 | * @param transport The type of transport used. 102 | * @return Zero on success, non-zero otherwise. 103 | */ 104 | int sk_timestamping_init(int fd, const char *device, enum timestamp_type type, 105 | enum transport_type transport); 106 | 107 | /** 108 | * Limits the time that RECVMSG(2) will poll while waiting for the tx timestamp 109 | * if MSG_ERRQUEUE is set. Specified in milliseconds. 110 | */ 111 | extern int sk_tx_timeout; 112 | 113 | /** 114 | * Enables the SO_TIMESTAMPNS socket option on the both the event and 115 | * general sockets in order to test the order of paired sync and 116 | * follow up messages using their network stack receipt time stamps. 117 | */ 118 | extern int sk_check_fupsync; 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /stats.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file stats.c 3 | * @note Copyright (C) 2013 Miroslav Lichvar 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include 20 | #include 21 | #include 22 | 23 | #include "stats.h" 24 | 25 | struct stats { 26 | unsigned int num; 27 | double min; 28 | double max; 29 | double mean; 30 | double sum_sqr; 31 | double sum_diff_sqr; 32 | }; 33 | 34 | struct stats *stats_create(void) 35 | { 36 | struct stats *stats; 37 | 38 | stats = calloc(1, sizeof *stats); 39 | return stats; 40 | } 41 | 42 | void stats_destroy(struct stats *stats) 43 | { 44 | free(stats); 45 | } 46 | 47 | void stats_add_value(struct stats *stats, double value) 48 | { 49 | double old_mean = stats->mean; 50 | 51 | if (!stats->num || stats->max < value) 52 | stats->max = value; 53 | if (!stats->num || stats->min > value) 54 | stats->min = value; 55 | 56 | stats->num++; 57 | stats->mean = old_mean + (value - old_mean) / stats->num; 58 | stats->sum_sqr += value * value; 59 | stats->sum_diff_sqr += (value - old_mean) * (value - stats->mean); 60 | } 61 | 62 | unsigned int stats_get_num_values(struct stats *stats) 63 | { 64 | return stats->num; 65 | } 66 | 67 | int stats_get_result(struct stats *stats, struct stats_result *result) 68 | { 69 | if (!stats->num) 70 | return -1; 71 | 72 | result->min = stats->min; 73 | result->max = stats->max; 74 | result->max_abs = stats->max > -stats->min ? stats->max : -stats->min; 75 | result->mean = stats->mean; 76 | result->rms = sqrt(stats->sum_sqr / stats->num); 77 | result->stddev = sqrt(stats->sum_diff_sqr / stats->num); 78 | 79 | return 0; 80 | } 81 | 82 | void stats_reset(struct stats *stats) 83 | { 84 | memset(stats, 0, sizeof *stats); 85 | } 86 | -------------------------------------------------------------------------------- /stats.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file stats.h 3 | * @brief Implements various statistics. 4 | * @note Copyright (C) 2013 Miroslav Lichvar 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_STATS_H 21 | #define HAVE_STATS_H 22 | 23 | /** Opaque type */ 24 | struct stats; 25 | 26 | /** 27 | * Create a new instance of statistics. 28 | * @return A pointer to a new stats on success, NULL otherwise. 29 | */ 30 | struct stats *stats_create(void); 31 | 32 | /** 33 | * Destroy an instance of stats. 34 | * @param servo Pointer to stats obtained via @ref stats_create(). 35 | */ 36 | void stats_destroy(struct stats *stats); 37 | 38 | /** 39 | * Add a new value to the stats. 40 | * @param stats Pointer to stats obtained via @ref stats_create(). 41 | * @param value The measured value. 42 | */ 43 | void stats_add_value(struct stats *stats, double value); 44 | 45 | /** 46 | * Get the number of values collected in the stats so far. 47 | * @param stats Pointer to stats obtained via @ref stats_create(). 48 | * @return The number of values. 49 | */ 50 | unsigned int stats_get_num_values(struct stats *stats); 51 | 52 | struct stats_result { 53 | double min; 54 | double max; 55 | double max_abs; 56 | double mean; 57 | double rms; 58 | double stddev; 59 | }; 60 | 61 | /** 62 | * Obtain the results of the calculated statistics. 63 | * @param stats Pointer to stats obtained via @ref stats_create(). 64 | * @param stats_result Pointer to stats_result to store the results. 65 | * @return Zero on success, non-zero if no values were added. 66 | */ 67 | int stats_get_result(struct stats *stats, struct stats_result *result); 68 | 69 | /** 70 | * Reset all statistics. 71 | * @param stats Pointer to stats obtained via @ref stats_create(). 72 | */ 73 | void stats_reset(struct stats *stats); 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /sysoff.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file sysoff.c 3 | * @brief Implements the system offset estimation method. 4 | * @note Copyright (C) 2012 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #include 21 | #include 22 | #include 23 | 24 | #include "sysoff.h" 25 | 26 | #define NS_PER_SEC 1000000000LL 27 | 28 | #ifdef PTP_SYS_OFFSET 29 | 30 | static int64_t pctns(struct ptp_clock_time *t) 31 | { 32 | return t->sec * NS_PER_SEC + t->nsec; 33 | } 34 | 35 | static struct { 36 | int64_t interval; 37 | int64_t offset; 38 | uint64_t timestamp; 39 | } samples[PTP_MAX_SAMPLES]; 40 | 41 | static void insertion_sort(int length, int64_t interval, int64_t offset, uint64_t ts) 42 | { 43 | int i = length - 1; 44 | while (i >= 0) { 45 | if (samples[i].interval < interval) 46 | break; 47 | samples[i+1] = samples[i]; 48 | i--; 49 | } 50 | samples[i+1].interval = interval; 51 | samples[i+1].offset = offset; 52 | samples[i+1].timestamp = ts; 53 | } 54 | 55 | static int64_t sysoff_estimate(struct ptp_clock_time *pct, int n_samples, 56 | uint64_t *ts, int64_t *delay) 57 | { 58 | int64_t t1, t2, tp; 59 | int64_t interval, offset; 60 | int i; 61 | 62 | for (i = 0; i < n_samples; i++) { 63 | t1 = pctns(&pct[2*i]); 64 | tp = pctns(&pct[2*i+1]); 65 | t2 = pctns(&pct[2*i+2]); 66 | interval = t2 - t1; 67 | offset = (t2 + t1) / 2 - tp; 68 | insertion_sort(i, interval, offset, (t2 + t1) / 2); 69 | } 70 | *ts = samples[0].timestamp; 71 | *delay = samples[0].interval; 72 | return samples[0].offset; 73 | } 74 | 75 | int sysoff_measure(int fd, int n_samples, 76 | int64_t *result, uint64_t *ts, int64_t *delay) 77 | { 78 | struct ptp_sys_offset pso; 79 | pso.n_samples = n_samples; 80 | if (ioctl(fd, PTP_SYS_OFFSET, &pso)) { 81 | perror("ioctl PTP_SYS_OFFSET"); 82 | return SYSOFF_RUN_TIME_MISSING; 83 | } 84 | *result = sysoff_estimate(pso.ts, n_samples, ts, delay); 85 | return SYSOFF_SUPPORTED; 86 | } 87 | 88 | int sysoff_probe(int fd, int n_samples) 89 | { 90 | int64_t junk, delay; 91 | uint64_t ts; 92 | 93 | if (n_samples > PTP_MAX_SAMPLES) { 94 | fprintf(stderr, "warning: %d exceeds kernel max readings %d\n", 95 | n_samples, PTP_MAX_SAMPLES); 96 | fprintf(stderr, "falling back to clock_gettime method\n"); 97 | return SYSOFF_RUN_TIME_MISSING; 98 | } 99 | 100 | return sysoff_measure(fd, n_samples, &junk, &ts, &delay); 101 | } 102 | 103 | #else /* !PTP_SYS_OFFSET */ 104 | 105 | int sysoff_measure(int fd, int n_samples, 106 | int64_t *result, uint64_t *ts, int64_t *delay) 107 | { 108 | return SYSOFF_COMPILE_TIME_MISSING; 109 | } 110 | 111 | int sysoff_probe(int fd, int n_samples) 112 | { 113 | return SYSOFF_COMPILE_TIME_MISSING; 114 | } 115 | 116 | #endif /* PTP_SYS_OFFSET */ 117 | -------------------------------------------------------------------------------- /sysoff.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file sysoff.h 3 | * @brief Implements the system offset estimation method. 4 | * @note Copyright (C) 2012 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | #include 22 | 23 | enum { 24 | SYSOFF_SUPPORTED, 25 | SYSOFF_COMPILE_TIME_MISSING, 26 | SYSOFF_RUN_TIME_MISSING, 27 | }; 28 | 29 | /** 30 | * Check to see if the PTP_SYS_OFFSET ioctl is supported. 31 | * @param fd An open file descriptor to a PHC device. 32 | * @return One of the SYSOFF_ enumeration values. 33 | */ 34 | int sysoff_probe(int fd, int n_samples); 35 | 36 | /** 37 | * Measure the offset between a PHC and the system time. 38 | * @param fd An open file descriptor to a PHC device. 39 | * @param n_samples The number of consecutive readings to make. 40 | * @param result The estimated offset in nanoseconds. 41 | * @param ts The system time corresponding to the 'result'. 42 | * @param delay The delay in reading of the clock in nanoseconds. 43 | * @return One of the SYSOFF_ enumeration values. 44 | */ 45 | int sysoff_measure(int fd, int n_samples, 46 | int64_t *result, uint64_t *ts, int64_t *delay); 47 | -------------------------------------------------------------------------------- /tlv.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tlv.h 3 | * @brief Implements helper routines for processing Type Length Value fields. 4 | * @note Copyright (C) 2012 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_TLV_H 21 | #define HAVE_TLV_H 22 | 23 | #include "ddt.h" 24 | #include "ds.h" 25 | 26 | /* TLV types */ 27 | #define TLV_MANAGEMENT 0x0001 28 | #define TLV_MANAGEMENT_ERROR_STATUS 0x0002 29 | #define TLV_ORGANIZATION_EXTENSION 0x0003 30 | #define TLV_REQUEST_UNICAST_TRANSMISSION 0x0004 31 | #define TLV_GRANT_UNICAST_TRANSMISSION 0x0005 32 | #define TLV_CANCEL_UNICAST_TRANSMISSION 0x0006 33 | #define TLV_ACKNOWLEDGE_CANCEL_UNICAST_TRANSMISSION 0x0007 34 | #define TLV_PATH_TRACE 0x0008 35 | #define TLV_ALTERNATE_TIME_OFFSET_INDICATOR 0x0009 36 | #define TLV_AUTHENTICATION 0x2000 37 | #define TLV_AUTHENTICATION_CHALLENGE 0x2001 38 | #define TLV_SECURITY_ASSOCIATION_UPDATE 0x2002 39 | #define TLV_CUM_FREQ_SCALE_FACTOR_OFFSET 0x2003 40 | 41 | enum management_action { 42 | GET, 43 | SET, 44 | RESPONSE, 45 | COMMAND, 46 | ACKNOWLEDGE, 47 | }; 48 | 49 | /* Clock management ID values */ 50 | #define USER_DESCRIPTION 0x0002 51 | #define SAVE_IN_NON_VOLATILE_STORAGE 0x0003 52 | #define RESET_NON_VOLATILE_STORAGE 0x0004 53 | #define INITIALIZE 0x0005 54 | #define FAULT_LOG 0x0006 55 | #define FAULT_LOG_RESET 0x0007 56 | #define DEFAULT_DATA_SET 0x2000 57 | #define CURRENT_DATA_SET 0x2001 58 | #define PARENT_DATA_SET 0x2002 59 | #define TIME_PROPERTIES_DATA_SET 0x2003 60 | #define PRIORITY1 0x2005 61 | #define PRIORITY2 0x2006 62 | #define DOMAIN 0x2007 63 | #define SLAVE_ONLY 0x2008 64 | #define TIME 0x200F 65 | #define CLOCK_ACCURACY 0x2010 66 | #define UTC_PROPERTIES 0x2011 67 | #define TRACEABILITY_PROPERTIES 0x2012 68 | #define TIMESCALE_PROPERTIES 0x2013 69 | #define PATH_TRACE_LIST 0x2015 70 | #define PATH_TRACE_ENABLE 0x2016 71 | #define GRANDMASTER_CLUSTER_TABLE 0x2017 72 | #define ACCEPTABLE_MASTER_TABLE 0x201A 73 | #define ACCEPTABLE_MASTER_MAX_TABLE_SIZE 0x201C 74 | #define ALTERNATE_TIME_OFFSET_ENABLE 0x201E 75 | #define ALTERNATE_TIME_OFFSET_NAME 0x201F 76 | #define ALTERNATE_TIME_OFFSET_MAX_KEY 0x2020 77 | #define ALTERNATE_TIME_OFFSET_PROPERTIES 0x2021 78 | #define TRANSPARENT_CLOCK_DEFAULT_DATA_SET 0x4000 79 | #define PRIMARY_DOMAIN 0x4002 80 | #define TIME_STATUS_NP 0xC000 81 | #define GRANDMASTER_SETTINGS_NP 0xC001 82 | #define SUBSCRIBE_EVENTS_NP 0xC003 83 | 84 | /* Port management ID values */ 85 | #define NULL_MANAGEMENT 0x0000 86 | #define CLOCK_DESCRIPTION 0x0001 87 | #define PORT_DATA_SET 0x2004 88 | #define LOG_ANNOUNCE_INTERVAL 0x2009 89 | #define ANNOUNCE_RECEIPT_TIMEOUT 0x200A 90 | #define LOG_SYNC_INTERVAL 0x200B 91 | #define VERSION_NUMBER 0x200C 92 | #define ENABLE_PORT 0x200D 93 | #define DISABLE_PORT 0x200E 94 | #define UNICAST_NEGOTIATION_ENABLE 0x2014 95 | #define UNICAST_MASTER_TABLE 0x2018 96 | #define UNICAST_MASTER_MAX_TABLE_SIZE 0x2019 97 | #define ACCEPTABLE_MASTER_TABLE_ENABLED 0x201B 98 | #define ALTERNATE_MASTER 0x201D 99 | #define TRANSPARENT_CLOCK_PORT_DATA_SET 0x4001 100 | #define DELAY_MECHANISM 0x6000 101 | #define LOG_MIN_PDELAY_REQ_INTERVAL 0x6001 102 | #define PORT_DATA_SET_NP 0xC002 103 | #define PORT_PROPERTIES_NP 0xC004 104 | 105 | /* Management error ID values */ 106 | #define RESPONSE_TOO_BIG 0x0001 107 | #define NO_SUCH_ID 0x0002 108 | #define WRONG_LENGTH 0x0003 109 | #define WRONG_VALUE 0x0004 110 | #define NOT_SETABLE 0x0005 111 | #define NOT_SUPPORTED 0x0006 112 | #define GENERAL_ERROR 0xFFFE 113 | 114 | struct management_tlv { 115 | Enumeration16 type; 116 | UInteger16 length; 117 | Enumeration16 id; 118 | Octet data[0]; 119 | } PACKED; 120 | 121 | struct management_tlv_datum { 122 | uint8_t val; 123 | uint8_t reserved; 124 | } PACKED; 125 | 126 | struct management_error_status { 127 | Enumeration16 type; 128 | UInteger16 length; 129 | Enumeration16 error; 130 | Enumeration16 id; 131 | Octet reserved[4]; 132 | Octet data[0]; 133 | } PACKED; 134 | 135 | /* Organizationally Unique Identifiers */ 136 | #define IEEE_802_1_COMMITTEE 0x00, 0x80, 0xC2 137 | extern uint8_t ieee8021_id[3]; 138 | 139 | struct organization_tlv { 140 | Enumeration16 type; 141 | UInteger16 length; 142 | Octet id[3]; 143 | Octet subtype[3]; 144 | } PACKED; 145 | 146 | #define PATH_TRACE_MAX \ 147 | ((sizeof(struct message_data) - sizeof(struct announce_msg) - sizeof(struct TLV)) / \ 148 | sizeof(struct ClockIdentity)) 149 | 150 | struct path_trace_tlv { 151 | Enumeration16 type; 152 | UInteger16 length; 153 | struct ClockIdentity cid[0]; 154 | } PACKED; 155 | 156 | static inline unsigned int path_length(struct path_trace_tlv *p) 157 | { 158 | return p->length / sizeof(struct ClockIdentity); 159 | } 160 | 161 | typedef struct Integer96 { 162 | uint16_t nanoseconds_msb; 163 | uint64_t nanoseconds_lsb; 164 | uint16_t fractional_nanoseconds; 165 | } PACKED ScaledNs; 166 | 167 | struct follow_up_info_tlv { 168 | Enumeration16 type; 169 | UInteger16 length; 170 | Octet id[3]; 171 | Octet subtype[3]; 172 | Integer32 cumulativeScaledRateOffset; 173 | UInteger16 gmTimeBaseIndicator; 174 | ScaledNs lastGmPhaseChange; 175 | Integer32 scaledLastGmFreqChange; 176 | } PACKED; 177 | 178 | struct time_status_np { 179 | int64_t master_offset; /*nanoseconds*/ 180 | int64_t ingress_time; /*nanoseconds*/ 181 | Integer32 cumulativeScaledRateOffset; 182 | Integer32 scaledLastGmFreqChange; 183 | UInteger16 gmTimeBaseIndicator; 184 | ScaledNs lastGmPhaseChange; 185 | Integer32 gmPresent; 186 | struct ClockIdentity gmIdentity; 187 | } PACKED; 188 | 189 | struct grandmaster_settings_np { 190 | struct ClockQuality clockQuality; 191 | Integer16 utc_offset; 192 | UInteger8 time_flags; 193 | Enumeration8 time_source; 194 | } PACKED; 195 | 196 | struct port_ds_np { 197 | UInteger32 neighborPropDelayThresh; /*nanoseconds*/ 198 | Integer32 asCapable; 199 | } PACKED; 200 | 201 | 202 | #define EVENT_BITMASK_CNT 64 203 | 204 | struct subscribe_events_np { 205 | uint16_t duration; /* seconds */ 206 | uint8_t bitmask[EVENT_BITMASK_CNT]; 207 | } PACKED; 208 | 209 | struct port_properties_np { 210 | struct PortIdentity portIdentity; 211 | uint8_t port_state; 212 | uint8_t timestamping; 213 | struct PTPText interface; 214 | } PACKED; 215 | 216 | enum clock_type { 217 | CLOCK_TYPE_ORDINARY = 0x8000, 218 | CLOCK_TYPE_BOUNDARY = 0x4000, 219 | CLOCK_TYPE_P2P = 0x2000, 220 | CLOCK_TYPE_E2E = 0x1000, 221 | CLOCK_TYPE_MANAGEMENT = 0x0800, 222 | }; 223 | 224 | #define PROFILE_ID_LEN 6 225 | 226 | struct mgmt_clock_description { 227 | UInteger16 *clockType; 228 | struct PTPText *physicalLayerProtocol; 229 | struct PhysicalAddress *physicalAddress; 230 | struct PortAddress *protocolAddress; 231 | Octet *manufacturerIdentity; 232 | struct PTPText *productDescription; 233 | struct PTPText *revisionData; 234 | struct PTPText *userDescription; 235 | Octet *profileIdentity; 236 | }; 237 | 238 | struct tlv_extra { 239 | union { 240 | struct mgmt_clock_description cd; 241 | }; 242 | }; 243 | 244 | /** 245 | * Converts recognized value sub-fields into host byte order. 246 | * @param tlv Pointer to a Type Length Value field. 247 | * @param extra Additional struct where data from tlv will be saved, 248 | * can be NULL. 249 | * @return Zero if successful, otherwise non-zero 250 | */ 251 | int tlv_post_recv(struct TLV *tlv, struct tlv_extra *extra); 252 | 253 | /** 254 | * Converts recognized value sub-fields into network byte order. 255 | * @param tlv Pointer to a Type Length Value field. 256 | * @param extra Additional struct containing tlv data to send, can be 257 | * NULL. 258 | */ 259 | void tlv_pre_send(struct TLV *tlv, struct tlv_extra *extra); 260 | 261 | #endif 262 | -------------------------------------------------------------------------------- /tmv.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tmv.h 3 | * @brief Implements an abstract time value type. 4 | * @note Copyright (C) 2011 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_TMV_H 21 | #define HAVE_TMV_H 22 | 23 | #include 24 | 25 | #include "ddt.h" 26 | #include "pdt.h" 27 | 28 | #define NS_PER_SEC 1000000000LL 29 | 30 | /** 31 | * We implement the time value as a 64 bit signed integer containing 32 | * nanoseconds. Using this representation, we could really spare the 33 | * arithmetic functions such as @ref tmv_add() and the like, and just 34 | * use plain old math operators in the code. 35 | * 36 | * However, we are going to be a bit pedantic here and enforce the 37 | * use of the these functions, so that we can easily upgrade the code 38 | * to a finer representation later on. In that way, we can make use of 39 | * the fractional nanosecond parts of the correction fields, if and 40 | * when people start asking for them. 41 | */ 42 | typedef int64_t tmv_t; 43 | 44 | static inline tmv_t tmv_add(tmv_t a, tmv_t b) 45 | { 46 | return a + b; 47 | } 48 | 49 | static inline tmv_t tmv_div(tmv_t a, int divisor) 50 | { 51 | return a / divisor; 52 | } 53 | 54 | static inline int tmv_eq(tmv_t a, tmv_t b) 55 | { 56 | return a == b ? 1 : 0; 57 | } 58 | 59 | static inline int tmv_is_zero(tmv_t x) 60 | { 61 | return x == ((tmv_t) 0) ? 1 : 0; 62 | } 63 | 64 | static inline tmv_t tmv_sub(tmv_t a, tmv_t b) 65 | { 66 | return a - b; 67 | } 68 | 69 | static inline tmv_t tmv_zero(void) 70 | { 71 | return (tmv_t) 0; 72 | } 73 | 74 | static inline tmv_t correction_to_tmv(Integer64 c) 75 | { 76 | return c >> 16; 77 | } 78 | 79 | static inline double tmv_dbl(tmv_t x) 80 | { 81 | return (double) x; 82 | } 83 | 84 | static inline tmv_t dbl_tmv(double x) 85 | { 86 | return (tmv_t) x; 87 | } 88 | 89 | static inline int64_t tmv_to_nanoseconds(tmv_t x) 90 | { 91 | return x; 92 | } 93 | 94 | static inline TimeInterval tmv_to_TimeInterval(tmv_t x) 95 | { 96 | return x << 16; 97 | } 98 | 99 | static inline tmv_t timespec_to_tmv(struct timespec ts) 100 | { 101 | return ts.tv_sec * NS_PER_SEC + ts.tv_nsec; 102 | } 103 | 104 | static inline tmv_t timestamp_to_tmv(struct timestamp ts) 105 | { 106 | return ts.sec * NS_PER_SEC + ts.nsec; 107 | } 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /transport.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file transport.c 3 | * @note Copyright (C) 2011 Richard Cochran 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | 20 | #include 21 | 22 | #include "transport.h" 23 | #include "transport_private.h" 24 | #include "raw.h" 25 | #include "udp.h" 26 | #include "udp6.h" 27 | #include "uds.h" 28 | 29 | int transport_close(struct transport *t, struct fdarray *fda) 30 | { 31 | return t->close(t, fda); 32 | } 33 | 34 | int transport_open(struct transport *t, const char *name, 35 | struct fdarray *fda, enum timestamp_type tt) 36 | { 37 | return t->open(t, name, fda, tt); 38 | } 39 | 40 | int transport_recv(struct transport *t, int fd, struct ptp_message *msg) 41 | { 42 | return t->recv(t, fd, msg, sizeof(msg->data), &msg->address, &msg->hwts); 43 | } 44 | 45 | int transport_send(struct transport *t, struct fdarray *fda, int event, 46 | struct ptp_message *msg) 47 | { 48 | int len = ntohs(msg->header.messageLength); 49 | 50 | return t->send(t, fda, event, 0, msg, len, NULL, &msg->hwts); 51 | } 52 | 53 | int transport_peer(struct transport *t, struct fdarray *fda, int event, 54 | struct ptp_message *msg) 55 | { 56 | int len = ntohs(msg->header.messageLength); 57 | 58 | return t->send(t, fda, event, 1, msg, len, NULL, &msg->hwts); 59 | } 60 | 61 | int transport_sendto(struct transport *t, struct fdarray *fda, int event, 62 | struct ptp_message *msg) 63 | { 64 | int len = ntohs(msg->header.messageLength); 65 | 66 | return t->send(t, fda, event, 0, msg, len, &msg->address, &msg->hwts); 67 | } 68 | 69 | int transport_physical_addr(struct transport *t, uint8_t *addr) 70 | { 71 | if (t->physical_addr) { 72 | return t->physical_addr(t, addr); 73 | } 74 | return 0; 75 | } 76 | 77 | int transport_protocol_addr(struct transport *t, uint8_t *addr) 78 | { 79 | if (t->protocol_addr) { 80 | return t->protocol_addr(t, addr); 81 | } 82 | return 0; 83 | } 84 | 85 | enum transport_type transport_type(struct transport *t) 86 | { 87 | return t->type; 88 | } 89 | 90 | struct transport *transport_create(enum transport_type type) 91 | { 92 | struct transport *t = NULL; 93 | switch (type) { 94 | case TRANS_UDS: 95 | t = uds_transport_create(); 96 | break; 97 | case TRANS_UDP_IPV4: 98 | t = udp_transport_create(); 99 | break; 100 | case TRANS_UDP_IPV6: 101 | t = udp6_transport_create(); 102 | break; 103 | case TRANS_IEEE_802_3: 104 | t = raw_transport_create(); 105 | break; 106 | case TRANS_DEVICENET: 107 | case TRANS_CONTROLNET: 108 | case TRANS_PROFINET: 109 | break; 110 | } 111 | if (t) 112 | t->type = type; 113 | return t; 114 | } 115 | 116 | void transport_destroy(struct transport *t) 117 | { 118 | t->release(t); 119 | } 120 | -------------------------------------------------------------------------------- /transport.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file transport.h 3 | * @brief Defines an abstract transport layer. 4 | * @note Copyright (C) 2011 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_TRANSPORT_H 21 | #define HAVE_TRANSPORT_H 22 | 23 | #include 24 | #include 25 | 26 | #include "fd.h" 27 | #include "msg.h" 28 | 29 | /* Values from networkProtocol enumeration 7.4.1 Table 3 */ 30 | enum transport_type { 31 | /* 0 is Reserved in spec. Use it for UDS */ 32 | TRANS_UDS = 0, 33 | TRANS_UDP_IPV4 = 1, 34 | TRANS_UDP_IPV6, 35 | TRANS_IEEE_802_3, 36 | TRANS_DEVICENET, 37 | TRANS_CONTROLNET, 38 | TRANS_PROFINET, 39 | }; 40 | 41 | /** 42 | * Values for the 'event' parameter in transport_send() and 43 | * transport_peer(). 44 | */ 45 | enum transport_event { 46 | TRANS_GENERAL, 47 | TRANS_EVENT, 48 | TRANS_ONESTEP, 49 | }; 50 | 51 | struct transport; 52 | 53 | int transport_close(struct transport *t, struct fdarray *fda); 54 | 55 | int transport_open(struct transport *t, const char *name, 56 | struct fdarray *fda, enum timestamp_type tt); 57 | 58 | int transport_recv(struct transport *t, int fd, struct ptp_message *msg); 59 | 60 | /** 61 | * Sends the PTP message using the given transport. The message is sent to 62 | * the default (usually multicast) address, any address field in the 63 | * ptp_message itself is ignored. 64 | * @param t The transport. 65 | * @param fda The array of descriptors filled in by transport_open. 66 | * @param event 1 for event message, 0 for general message. 67 | * @param msg The message to send. 68 | * @return Number of bytes send, or negative value in case of an error. 69 | */ 70 | int transport_send(struct transport *t, struct fdarray *fda, int event, 71 | struct ptp_message *msg); 72 | 73 | /** 74 | * Sends the PTP message using the given transport. The message is sent to 75 | * the address used for p2p delay measurements (usually a multicast 76 | * address), any address field in the ptp_message itself is ignored. 77 | * @param t The transport. 78 | * @param fda The array of descriptors filled in by transport_open. 79 | * @param event 1 for event message, 0 for general message. 80 | * @param msg The message to send. 81 | * @return Number of bytes send, or negative value in case of an error. 82 | */ 83 | int transport_peer(struct transport *t, struct fdarray *fda, int event, 84 | struct ptp_message *msg); 85 | 86 | /** 87 | * Sends the PTP message using the given transport. The address has to be 88 | * provided in the address field of the message. 89 | * @param t The transport. 90 | * @param fda The array of descriptors filled in by transport_open. 91 | * @param event 1 for event message, 0 for general message. 92 | * @param msg The message to send. The address of the destination has to 93 | * be set in the address field. 94 | * @return Number of bytes send, or negative value in case of an error. 95 | */ 96 | int transport_sendto(struct transport *t, struct fdarray *fda, int event, 97 | struct ptp_message *msg); 98 | 99 | /** 100 | * Returns the transport's type. 101 | */ 102 | enum transport_type transport_type(struct transport *t); 103 | 104 | #define TRANSPORT_ADDR_LEN 16 105 | 106 | /** 107 | * Gets the transport's physical address. 108 | * @param t The transport. 109 | * @param addr The address will be written to this buffer. 110 | * @return The number of bytes written to the buffer. Will be 0-16 111 | * bytes 112 | */ 113 | int transport_physical_addr(struct transport *t, uint8_t *addr); 114 | 115 | /** 116 | * Gets the transport's protocol address. 117 | * @param t The transport. 118 | * @param addr The address will be written to this buffer. 119 | * @return The number of bytes written to the buffer. Will be 0-16 120 | * bytes 121 | */ 122 | int transport_protocol_addr(struct transport *t, uint8_t *addr); 123 | 124 | /** 125 | * Allocate an instance of the specified transport. 126 | * @param type Which transport to obtain. 127 | * @return Pointer to a transport instance on success, NULL otherwise. 128 | */ 129 | struct transport *transport_create(enum transport_type type); 130 | 131 | /** 132 | * Free an instance of a transport. 133 | * @param t Pointer obtained by calling transport_create(). 134 | */ 135 | void transport_destroy(struct transport *t); 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /transport_private.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file transport_private.h 3 | * @brief Defines a private interface for the abstract transport layer. 4 | * @note Copyright (C) 2012 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_TRANSPORT_PRIVATE_H 21 | #define HAVE_TRANSPORT_PRIVATE_H 22 | 23 | #include 24 | 25 | #include "address.h" 26 | #include "fd.h" 27 | #include "transport.h" 28 | 29 | struct transport { 30 | enum transport_type type; 31 | 32 | int (*close)(struct transport *t, struct fdarray *fda); 33 | 34 | int (*open)(struct transport *t, const char *name, struct fdarray *fda, 35 | enum timestamp_type tt); 36 | 37 | int (*recv)(struct transport *t, int fd, void *buf, int buflen, 38 | struct address *addr, struct hw_timestamp *hwts); 39 | 40 | int (*send)(struct transport *t, struct fdarray *fda, int event, 41 | int peer, void *buf, int buflen, struct address *addr, 42 | struct hw_timestamp *hwts); 43 | 44 | void (*release)(struct transport *t); 45 | 46 | int (*physical_addr)(struct transport *t, uint8_t *addr); 47 | 48 | int (*protocol_addr)(struct transport *t, uint8_t *addr); 49 | }; 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /udp.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file udp.c 3 | * @note Copyright (C) 2011 Richard Cochran 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "address.h" 33 | #include "contain.h" 34 | #include "print.h" 35 | #include "sk.h" 36 | #include "ether.h" 37 | #include "transport_private.h" 38 | #include "udp.h" 39 | 40 | #define EVENT_PORT 319 41 | #define GENERAL_PORT 320 42 | #define PTP_PRIMARY_MCAST_IPADDR "224.0.1.129" 43 | #define PTP_PDELAY_MCAST_IPADDR "224.0.0.107" 44 | 45 | struct udp { 46 | struct transport t; 47 | struct address ip; 48 | struct address mac; 49 | }; 50 | 51 | static int mcast_bind(int fd, int index) 52 | { 53 | int err; 54 | struct ip_mreqn req; 55 | memset(&req, 0, sizeof(req)); 56 | req.imr_ifindex = index; 57 | err = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &req, sizeof(req)); 58 | if (err) { 59 | pr_err("setsockopt IP_MULTICAST_IF failed: %m"); 60 | return -1; 61 | } 62 | return 0; 63 | } 64 | 65 | static int mcast_join(int fd, int index, const struct sockaddr *grp, 66 | socklen_t grplen) 67 | { 68 | int err, off = 0; 69 | struct ip_mreqn req; 70 | struct sockaddr_in *sa = (struct sockaddr_in *) grp; 71 | 72 | memset(&req, 0, sizeof(req)); 73 | memcpy(&req.imr_multiaddr, &sa->sin_addr, sizeof(struct in_addr)); 74 | req.imr_ifindex = index; 75 | err = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &req, sizeof(req)); 76 | if (err) { 77 | pr_err("setsockopt IP_ADD_MEMBERSHIP failed: %m"); 78 | return -1; 79 | } 80 | err = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &off, sizeof(off)); 81 | if (err) { 82 | pr_err("setsockopt IP_MULTICAST_LOOP failed: %m"); 83 | return -1; 84 | } 85 | return 0; 86 | } 87 | 88 | static int udp_close(struct transport *t, struct fdarray *fda) 89 | { 90 | close(fda->fd[0]); 91 | close(fda->fd[1]); 92 | return 0; 93 | } 94 | 95 | static int open_socket(const char *name, struct in_addr mc_addr[2], short port) 96 | { 97 | struct sockaddr_in addr; 98 | int fd, index, on = 1; 99 | 100 | memset(&addr, 0, sizeof(addr)); 101 | addr.sin_family = AF_INET; 102 | addr.sin_addr.s_addr = htonl(INADDR_ANY); 103 | addr.sin_port = htons(port); 104 | 105 | fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); 106 | if (fd < 0) { 107 | pr_err("socket failed: %m"); 108 | goto no_socket; 109 | } 110 | index = sk_interface_index(fd, name); 111 | if (index < 0) 112 | goto no_option; 113 | 114 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) { 115 | pr_err("setsockopt SO_REUSEADDR failed: %m"); 116 | goto no_option; 117 | } 118 | if (bind(fd, (struct sockaddr *) &addr, sizeof(addr))) { 119 | pr_err("bind failed: %m"); 120 | goto no_option; 121 | } 122 | if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name))) { 123 | pr_err("setsockopt SO_BINDTODEVICE failed: %m"); 124 | goto no_option; 125 | } 126 | addr.sin_addr = mc_addr[0]; 127 | if (mcast_join(fd, index, (struct sockaddr *) &addr, sizeof(addr))) { 128 | pr_err("mcast_join failed"); 129 | goto no_option; 130 | } 131 | addr.sin_addr = mc_addr[1]; 132 | if (mcast_join(fd, index, (struct sockaddr *) &addr, sizeof(addr))) { 133 | pr_err("mcast_join failed"); 134 | goto no_option; 135 | } 136 | if (mcast_bind(fd, index)) { 137 | goto no_option; 138 | } 139 | return fd; 140 | no_option: 141 | close(fd); 142 | no_socket: 143 | return -1; 144 | } 145 | 146 | enum { MC_PRIMARY, MC_PDELAY }; 147 | 148 | static struct in_addr mcast_addr[2]; 149 | 150 | static int udp_open(struct transport *t, const char *name, struct fdarray *fda, 151 | enum timestamp_type ts_type) 152 | { 153 | struct udp *udp = container_of(t, struct udp, t); 154 | int efd, gfd; 155 | 156 | udp->mac.len = 0; 157 | sk_interface_macaddr(name, &udp->mac); 158 | 159 | udp->ip.len = 0; 160 | sk_interface_addr(name, AF_INET, &udp->ip); 161 | 162 | if (!inet_aton(PTP_PRIMARY_MCAST_IPADDR, &mcast_addr[MC_PRIMARY])) 163 | return -1; 164 | 165 | if (!inet_aton(PTP_PDELAY_MCAST_IPADDR, &mcast_addr[MC_PDELAY])) 166 | return -1; 167 | 168 | efd = open_socket(name, mcast_addr, EVENT_PORT); 169 | if (efd < 0) 170 | goto no_event; 171 | 172 | gfd = open_socket(name, mcast_addr, GENERAL_PORT); 173 | if (gfd < 0) 174 | goto no_general; 175 | 176 | if (sk_timestamping_init(efd, name, ts_type, TRANS_UDP_IPV4)) 177 | goto no_timestamping; 178 | 179 | if (sk_general_init(gfd)) 180 | goto no_timestamping; 181 | 182 | fda->fd[FD_EVENT] = efd; 183 | fda->fd[FD_GENERAL] = gfd; 184 | return 0; 185 | 186 | no_timestamping: 187 | close(gfd); 188 | no_general: 189 | close(efd); 190 | no_event: 191 | return -1; 192 | } 193 | 194 | static int udp_recv(struct transport *t, int fd, void *buf, int buflen, 195 | struct address *addr, struct hw_timestamp *hwts) 196 | { 197 | return sk_receive(fd, buf, buflen, addr, hwts, 0); 198 | } 199 | 200 | static int udp_send(struct transport *t, struct fdarray *fda, int event, 201 | int peer, void *buf, int len, struct address *addr, 202 | struct hw_timestamp *hwts) 203 | { 204 | ssize_t cnt; 205 | int fd = event ? fda->fd[FD_EVENT] : fda->fd[FD_GENERAL]; 206 | struct address addr_buf; 207 | unsigned char junk[1600]; 208 | 209 | if (!addr) { 210 | memset(&addr_buf, 0, sizeof(addr_buf)); 211 | addr_buf.sin.sin_family = AF_INET; 212 | addr_buf.sin.sin_addr = peer ? mcast_addr[MC_PDELAY] : 213 | mcast_addr[MC_PRIMARY]; 214 | addr_buf.sin.sin_port = htons(event ? EVENT_PORT : 215 | GENERAL_PORT); 216 | addr_buf.len = sizeof(addr_buf.sin); 217 | addr = &addr_buf; 218 | } 219 | 220 | /* 221 | * Extend the payload by two, for UDP checksum correction. 222 | * This is not really part of the standard, but it is the way 223 | * that the phyter works. 224 | */ 225 | if (event == TRANS_ONESTEP) 226 | len += 2; 227 | 228 | cnt = sendto(fd, buf, len, 0, &addr->sa, sizeof(addr->sin)); 229 | if (cnt < 1) { 230 | pr_err("sendto failed: %m"); 231 | return cnt; 232 | } 233 | /* 234 | * Get the time stamp right away. 235 | */ 236 | return event == TRANS_EVENT ? sk_receive(fd, junk, len, NULL, hwts, MSG_ERRQUEUE) : cnt; 237 | } 238 | 239 | static void udp_release(struct transport *t) 240 | { 241 | struct udp *udp = container_of(t, struct udp, t); 242 | free(udp); 243 | } 244 | 245 | static int udp_physical_addr(struct transport *t, uint8_t *addr) 246 | { 247 | struct udp *udp = container_of(t, struct udp, t); 248 | int len = 0; 249 | 250 | if (udp->mac.len) { 251 | len = MAC_LEN; 252 | memcpy(addr, udp->mac.sa.sa_data, len); 253 | } 254 | return len; 255 | } 256 | 257 | static int udp_protocol_addr(struct transport *t, uint8_t *addr) 258 | { 259 | struct udp *udp = container_of(t, struct udp, t); 260 | int len = 0; 261 | 262 | if (udp->ip.len) { 263 | len = sizeof(udp->ip.sin.sin_addr.s_addr); 264 | memcpy(addr, &udp->ip.sin.sin_addr.s_addr, len); 265 | } 266 | return len; 267 | } 268 | 269 | struct transport *udp_transport_create(void) 270 | { 271 | struct udp *udp = calloc(1, sizeof(*udp)); 272 | if (!udp) 273 | return NULL; 274 | udp->t.close = udp_close; 275 | udp->t.open = udp_open; 276 | udp->t.recv = udp_recv; 277 | udp->t.send = udp_send; 278 | udp->t.release = udp_release; 279 | udp->t.physical_addr = udp_physical_addr; 280 | udp->t.protocol_addr = udp_protocol_addr; 281 | return &udp->t; 282 | } 283 | -------------------------------------------------------------------------------- /udp.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file udp.h 3 | * @brief Implements transport over IPv4 UDP. 4 | * @note Copyright (C) 2011 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_UPD_H 21 | #define HAVE_UPD_H 22 | 23 | #include "fd.h" 24 | #include "transport.h" 25 | 26 | /** 27 | * Allocate an instance of a UDP/IPv4 transport. 28 | * @return Pointer to a new transport instance on success, NULL otherwise. 29 | */ 30 | struct transport *udp_transport_create(void); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /udp6.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file udp6.c 3 | * @note Copyright (C) 2012 Richard Cochran 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "address.h" 33 | #include "contain.h" 34 | #include "print.h" 35 | #include "sk.h" 36 | #include "ether.h" 37 | #include "transport_private.h" 38 | #include "udp6.h" 39 | 40 | #define EVENT_PORT 319 41 | #define GENERAL_PORT 320 42 | #define PTP_PRIMARY_MCAST_IP6ADDR "FF0E:0:0:0:0:0:0:181" 43 | #define PTP_PDELAY_MCAST_IP6ADDR "FF02:0:0:0:0:0:0:6B" 44 | 45 | unsigned char udp6_scope = 0x0E; 46 | 47 | struct udp6 { 48 | struct transport t; 49 | int index; 50 | struct address ip; 51 | struct address mac; 52 | }; 53 | 54 | static int is_link_local(struct in6_addr *addr) 55 | { 56 | return addr->s6_addr[1] == 0x02 ? 1 : 0; 57 | } 58 | 59 | static int mc_bind(int fd, int index) 60 | { 61 | int err; 62 | struct ipv6_mreq req; 63 | memset(&req, 0, sizeof(req)); 64 | req.ipv6mr_interface = index; 65 | err = setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &req, sizeof(req)); 66 | if (err) { 67 | pr_err("setsockopt IPV6_MULTICAST_IF failed: %m"); 68 | return -1; 69 | } 70 | return 0; 71 | } 72 | 73 | static int mc_join(int fd, int index, const struct sockaddr *grp, socklen_t grplen) 74 | { 75 | int err, off = 0; 76 | struct ipv6_mreq req; 77 | struct sockaddr_in6 *sa = (struct sockaddr_in6 *) grp; 78 | 79 | memset(&req, 0, sizeof(req)); 80 | memcpy(&req.ipv6mr_multiaddr, &sa->sin6_addr, sizeof(struct in6_addr)); 81 | req.ipv6mr_interface = index; 82 | err = setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &req, sizeof(req)); 83 | if (err) { 84 | pr_err("setsockopt IPV6_ADD_MEMBERSHIP failed: %m"); 85 | return -1; 86 | } 87 | err = setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off)); 88 | if (err) { 89 | pr_err("setsockopt IPV6_MULTICAST_LOOP failed: %m"); 90 | return -1; 91 | } 92 | return 0; 93 | } 94 | 95 | static int udp6_close(struct transport *t, struct fdarray *fda) 96 | { 97 | close(fda->fd[0]); 98 | close(fda->fd[1]); 99 | return 0; 100 | } 101 | 102 | static int open_socket_ipv6(const char *name, struct in6_addr mc_addr[2], short port, 103 | int *interface_index) 104 | { 105 | struct sockaddr_in6 addr; 106 | int fd, index, on = 1; 107 | 108 | memset(&addr, 0, sizeof(addr)); 109 | addr.sin6_family = AF_INET6; 110 | addr.sin6_addr = in6addr_any; 111 | addr.sin6_port = htons(port); 112 | 113 | fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); 114 | if (fd < 0) { 115 | pr_err("socket failed: %m"); 116 | goto no_socket; 117 | } 118 | index = sk_interface_index(fd, name); 119 | if (index < 0) 120 | goto no_option; 121 | 122 | *interface_index = index; 123 | 124 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) { 125 | pr_err("setsockopt SO_REUSEADDR failed: %m"); 126 | goto no_option; 127 | } 128 | if (bind(fd, (struct sockaddr *) &addr, sizeof(addr))) { 129 | pr_err("bind failed: %m"); 130 | goto no_option; 131 | } 132 | if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name))) { 133 | pr_err("setsockopt SO_BINDTODEVICE failed: %m"); 134 | goto no_option; 135 | } 136 | addr.sin6_addr = mc_addr[0]; 137 | if (mc_join(fd, index, (struct sockaddr *) &addr, sizeof(addr))) { 138 | pr_err("mcast_join failed"); 139 | goto no_option; 140 | } 141 | addr.sin6_addr = mc_addr[1]; 142 | if (mc_join(fd, index, (struct sockaddr *) &addr, sizeof(addr))) { 143 | pr_err("mcast_join failed"); 144 | goto no_option; 145 | } 146 | if (mc_bind(fd, index)) { 147 | goto no_option; 148 | } 149 | return fd; 150 | no_option: 151 | close(fd); 152 | no_socket: 153 | return -1; 154 | } 155 | 156 | enum { MC_PRIMARY, MC_PDELAY }; 157 | 158 | static struct in6_addr mc6_addr[2]; 159 | 160 | static int udp6_open(struct transport *t, const char *name, struct fdarray *fda, 161 | enum timestamp_type ts_type) 162 | { 163 | struct udp6 *udp6 = container_of(t, struct udp6, t); 164 | int efd, gfd; 165 | 166 | udp6->mac.len = 0; 167 | sk_interface_macaddr(name, &udp6->mac); 168 | 169 | udp6->ip.len = 0; 170 | sk_interface_addr(name, AF_INET6, &udp6->ip); 171 | 172 | if (1 != inet_pton(AF_INET6, PTP_PRIMARY_MCAST_IP6ADDR, &mc6_addr[MC_PRIMARY])) 173 | return -1; 174 | 175 | mc6_addr[MC_PRIMARY].s6_addr[1] = udp6_scope; 176 | 177 | if (1 != inet_pton(AF_INET6, PTP_PDELAY_MCAST_IP6ADDR, &mc6_addr[MC_PDELAY])) 178 | return -1; 179 | 180 | efd = open_socket_ipv6(name, mc6_addr, EVENT_PORT, &udp6->index); 181 | if (efd < 0) 182 | goto no_event; 183 | 184 | gfd = open_socket_ipv6(name, mc6_addr, GENERAL_PORT, &udp6->index); 185 | if (gfd < 0) 186 | goto no_general; 187 | 188 | if (sk_timestamping_init(efd, name, ts_type, TRANS_UDP_IPV6)) 189 | goto no_timestamping; 190 | 191 | if (sk_general_init(gfd)) 192 | goto no_timestamping; 193 | 194 | fda->fd[FD_EVENT] = efd; 195 | fda->fd[FD_GENERAL] = gfd; 196 | return 0; 197 | 198 | no_timestamping: 199 | close(gfd); 200 | no_general: 201 | close(efd); 202 | no_event: 203 | return -1; 204 | } 205 | 206 | static int udp6_recv(struct transport *t, int fd, void *buf, int buflen, 207 | struct address *addr, struct hw_timestamp *hwts) 208 | { 209 | return sk_receive(fd, buf, buflen, addr, hwts, 0); 210 | } 211 | 212 | static int udp6_send(struct transport *t, struct fdarray *fda, int event, 213 | int peer, void *buf, int len, struct address *addr, 214 | struct hw_timestamp *hwts) 215 | { 216 | struct udp6 *udp6 = container_of(t, struct udp6, t); 217 | ssize_t cnt; 218 | int fd = event ? fda->fd[FD_EVENT] : fda->fd[FD_GENERAL]; 219 | struct address addr_buf; 220 | unsigned char junk[1600]; 221 | 222 | if (!addr) { 223 | memset(&addr_buf, 0, sizeof(addr_buf)); 224 | addr_buf.sin6.sin6_family = AF_INET6; 225 | addr_buf.sin6.sin6_addr = peer ? mc6_addr[MC_PDELAY] : 226 | mc6_addr[MC_PRIMARY]; 227 | addr_buf.sin6.sin6_port = htons(event ? EVENT_PORT : 228 | GENERAL_PORT); 229 | 230 | if (is_link_local(&addr_buf.sin6.sin6_addr)) 231 | addr_buf.sin6.sin6_scope_id = udp6->index; 232 | 233 | addr_buf.len = sizeof(addr_buf.sin6); 234 | addr = &addr_buf; 235 | } 236 | 237 | len += 2; /* Extend the payload by two, for UDP checksum corrections. */ 238 | 239 | cnt = sendto(fd, buf, len, 0, &addr->sa, sizeof(addr->sin6)); 240 | if (cnt < 1) { 241 | pr_err("sendto failed: %m"); 242 | return cnt; 243 | } 244 | /* 245 | * Get the time stamp right away. 246 | */ 247 | return event == TRANS_EVENT ? sk_receive(fd, junk, len, NULL, hwts, MSG_ERRQUEUE) : cnt; 248 | } 249 | 250 | static void udp6_release(struct transport *t) 251 | { 252 | struct udp6 *udp6 = container_of(t, struct udp6, t); 253 | free(udp6); 254 | } 255 | 256 | static int udp6_physical_addr(struct transport *t, uint8_t *addr) 257 | { 258 | struct udp6 *udp6 = container_of(t, struct udp6, t); 259 | int len = 0; 260 | 261 | if (udp6->mac.len) { 262 | len = MAC_LEN; 263 | memcpy(addr, udp6->mac.sa.sa_data, len); 264 | } 265 | return len; 266 | } 267 | 268 | static int udp6_protocol_addr(struct transport *t, uint8_t *addr) 269 | { 270 | struct udp6 *udp6 = container_of(t, struct udp6, t); 271 | int len = 0; 272 | 273 | if (udp6->ip.len) { 274 | len = sizeof(udp6->ip.sin6.sin6_addr.s6_addr); 275 | memcpy(addr, &udp6->ip.sin6.sin6_addr.s6_addr, len); 276 | } 277 | return len; 278 | } 279 | 280 | struct transport *udp6_transport_create(void) 281 | { 282 | struct udp6 *udp6; 283 | udp6 = calloc(1, sizeof(*udp6)); 284 | if (!udp6) 285 | return NULL; 286 | udp6->t.close = udp6_close; 287 | udp6->t.open = udp6_open; 288 | udp6->t.recv = udp6_recv; 289 | udp6->t.send = udp6_send; 290 | udp6->t.release = udp6_release; 291 | udp6->t.physical_addr = udp6_physical_addr; 292 | udp6->t.protocol_addr = udp6_protocol_addr; 293 | return &udp6->t; 294 | } 295 | -------------------------------------------------------------------------------- /udp6.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file udp6.h 3 | * @brief Implements transport over IPv6 UDP. 4 | * @note Copyright (C) 2012 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_UPD6_H 21 | #define HAVE_UPD6_H 22 | 23 | #include "fd.h" 24 | #include "transport.h" 25 | 26 | /** 27 | * The desired scope for the multicast messages. This will be used as 28 | * the second byte of the primary IPv6 address. See RFC 4291. 29 | */ 30 | extern unsigned char udp6_scope; 31 | 32 | /** 33 | * Allocate an instance of a UDP/IPv6 transport. 34 | * @return Pointer to a new transport instance on success, NULL otherwise. 35 | */ 36 | struct transport *udp6_transport_create(void); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /uds.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file uds.c 3 | * @note Copyright (C) 2012 Richard Cochran 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | */ 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "address.h" 29 | #include "contain.h" 30 | #include "print.h" 31 | #include "transport_private.h" 32 | #include "uds.h" 33 | 34 | char uds_path[MAX_IFNAME_SIZE + 1] = "/var/run/ptp4l"; 35 | 36 | #define UDS_FILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) /*0660*/ 37 | 38 | struct uds { 39 | struct transport t; 40 | struct address address; 41 | }; 42 | 43 | static int uds_close(struct transport *t, struct fdarray *fda) 44 | { 45 | close(fda->fd[FD_GENERAL]); 46 | return 0; 47 | } 48 | 49 | static int uds_open(struct transport *t, const char *name, struct fdarray *fda, 50 | enum timestamp_type tt) 51 | { 52 | int fd, err; 53 | struct sockaddr_un sa; 54 | struct uds *uds = container_of(t, struct uds, t); 55 | 56 | fd = socket(AF_LOCAL, SOCK_DGRAM, 0); 57 | if (fd < 0) { 58 | pr_err("uds: failed to create socket: %m"); 59 | return -1; 60 | } 61 | memset(&sa, 0, sizeof(sa)); 62 | sa.sun_family = AF_LOCAL; 63 | strncpy(sa.sun_path, name, sizeof(sa.sun_path) - 1); 64 | 65 | unlink(name); 66 | 67 | err = bind(fd, (struct sockaddr *) &sa, sizeof(sa)); 68 | if (err < 0) { 69 | pr_err("uds: bind failed: %m"); 70 | close(fd); 71 | return -1; 72 | } 73 | 74 | /* For client use, pre load the server path. */ 75 | memset(&sa, 0, sizeof(sa)); 76 | sa.sun_family = AF_LOCAL; 77 | strncpy(sa.sun_path, uds_path, sizeof(sa.sun_path) - 1); 78 | uds->address.sun = sa; 79 | uds->address.len = sizeof(sa); 80 | 81 | chmod(name, UDS_FILEMODE); 82 | fda->fd[FD_EVENT] = -1; 83 | fda->fd[FD_GENERAL] = fd; 84 | return 0; 85 | } 86 | 87 | static int uds_recv(struct transport *t, int fd, void *buf, int buflen, 88 | struct address *addr, struct hw_timestamp *hwts) 89 | { 90 | int cnt; 91 | struct uds *uds = container_of(t, struct uds, t); 92 | 93 | addr->len = sizeof(addr->sun); 94 | cnt = recvfrom(fd, buf, buflen, 0, &addr->sa, &addr->len); 95 | if (cnt <= 0) { 96 | pr_err("uds: recvfrom failed: %m"); 97 | return cnt; 98 | } 99 | uds->address = *addr; 100 | return cnt; 101 | } 102 | 103 | static int uds_send(struct transport *t, struct fdarray *fda, int event, 104 | int peer, void *buf, int buflen, struct address *addr, 105 | struct hw_timestamp *hwts) 106 | { 107 | int cnt, fd = fda->fd[FD_GENERAL]; 108 | struct uds *uds = container_of(t, struct uds, t); 109 | 110 | if (!addr) 111 | addr = &uds->address; 112 | 113 | cnt = sendto(fd, buf, buflen, 0, &addr->sa, addr->len); 114 | if (cnt <= 0 && errno != ECONNREFUSED) { 115 | pr_err("uds: sendto failed: %m"); 116 | } 117 | return cnt; 118 | } 119 | 120 | static void uds_release(struct transport *t) 121 | { 122 | struct uds *uds = container_of(t, struct uds, t); 123 | free(uds); 124 | } 125 | 126 | struct transport *uds_transport_create(void) 127 | { 128 | struct uds *uds; 129 | uds = calloc(1, sizeof(*uds)); 130 | if (!uds) 131 | return NULL; 132 | uds->t.close = uds_close; 133 | uds->t.open = uds_open; 134 | uds->t.recv = uds_recv; 135 | uds->t.send = uds_send; 136 | uds->t.release = uds_release; 137 | return &uds->t; 138 | } 139 | 140 | -------------------------------------------------------------------------------- /uds.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file uds.h 3 | * @brief Implements a management interface via UNIX domain sockets. 4 | * @note Copyright (C) 2012 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_UDS_H 21 | #define HAVE_UDS_H 22 | 23 | #include "config.h" 24 | #include "fd.h" 25 | #include "transport.h" 26 | 27 | /** 28 | * Address of the server. 29 | */ 30 | extern char uds_path[MAX_IFNAME_SIZE + 1]; 31 | 32 | /** 33 | * Allocate an instance of a UDS transport. 34 | * @return Pointer to a new transport instance on success, NULL otherwise. 35 | */ 36 | struct transport *uds_transport_create(void); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /version.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file version.c 3 | * @brief Provides a software version string. 4 | * @note Copyright (C) 2012 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #include "version.h" 21 | 22 | #define STRINGIFY_(x) #x 23 | #define STRINGIFY(x) STRINGIFY_(x) 24 | 25 | static const char *version = STRINGIFY(VER); 26 | 27 | void version_show(FILE *fp) 28 | { 29 | fprintf(fp, "%s\n", version); 30 | } 31 | 32 | const char *version_string(void) 33 | { 34 | return version; 35 | } 36 | -------------------------------------------------------------------------------- /version.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file version.h 3 | * @brief Provides a software version string. 4 | * @note Copyright (C) 2012 Richard Cochran 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | #ifndef HAVE_VERSION_H 21 | #define HAVE_VERSION_H 22 | 23 | #include 24 | 25 | /** 26 | * Print the software version string into the given file. 27 | * @param fp File pointer open for writing. 28 | */ 29 | void version_show(FILE *fp); 30 | 31 | /** 32 | * Provide the software version as a human readable string. 33 | * @return Pointer to a static global buffer holding the result. 34 | */ 35 | const char *version_string(void); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # This scripts takes the major and minor release numbers and adds 4 | # local version information from the git version control system. 5 | # Adapted from scripts/setlocalversion in the Linux kernel sources. 6 | # 7 | major=1 8 | minor=4 9 | extra= 10 | 11 | usage() { 12 | echo "Usage: $0 [srctree]" >&2 13 | exit 1 14 | } 15 | 16 | srctree=. 17 | if test $# -gt 0; then 18 | srctree=$1 19 | shift 20 | fi 21 | if test $# -gt 0 -o ! -d "$srctree"; then 22 | usage 23 | fi 24 | 25 | scm_version() 26 | { 27 | cd "$srctree" 28 | 29 | # Check for git and a git repo. 30 | if test -d .git && head=`git rev-parse --verify --short HEAD 2>/dev/null`; then 31 | 32 | # If we are at a tagged commit (like "v2.6.30-rc6"), we ignore 33 | # it, because this version is defined in the top level Makefile. 34 | if [ -z "`git describe --exact-match 2>/dev/null`" ]; then 35 | 36 | # If we are past a tagged commit (like 37 | # "v2.6.30-rc5-302-g72357d5"), we pretty print it. 38 | if atag="`git describe 2>/dev/null`"; then 39 | echo "$atag" | awk -F- '{printf("-%05d-%s", $(NF-1),$(NF))}' 40 | 41 | # If we don't have a tag at all we print -g{commitish}. 42 | else 43 | printf '%s%s' -g $head 44 | fi 45 | fi 46 | 47 | # Update index only on r/w media 48 | [ -w . ] && git update-index --refresh --unmerged > /dev/null 49 | 50 | # Check for uncommitted changes 51 | if git diff-index --name-only HEAD | grep -qv "^scripts/package"; then 52 | printf '%s' -dirty 53 | fi 54 | 55 | # All done with git 56 | return 57 | fi 58 | } 59 | 60 | res="${major}.${minor}${extra}$(scm_version)" 61 | echo "$res" 62 | --------------------------------------------------------------------------------